# Frontend Redesign Implementation Plan

> **Status:** COMPLETED AND DEPLOYED (2026-02-09)
> All phases implemented, bug-fixed, and deployed to GKE. See commits b9a0f98 through cdef60b.

**Goal:** Implement a unified design system and redesigned layouts across all 3 Monetarie Vue frontends (banking, merchant, admin), adding new pages inspired by Owem portal benchmark.

**Architecture:** Extend `@monetarie/shared` with new `theme/` and `components/` subpaths exporting CSS design tokens, a theme composable, and 6 shared Vue components. Each app adopts the new layout shell (FlxSidebar + FlxHeader + mobile nav) and gains new pages (analytics dashboard, MEDs, fees, limits, ledger, transaction search).

**Tech Stack:** Vue 3, PrimeVue 4 Aura, Lucide Vue Next, TypeScript, CSS custom properties, TanStack Vue Query, Pinia

---

## Task 1: Create Design Token CSS file

**Files:**
- Create: `packages/shared/src/theme/tokens.css`

**Step 1: Create the tokens CSS file**

```css
/* packages/shared/src/theme/tokens.css */

/* ═══════════════════════════════════════════════════════════
   Monetarie Design Tokens — Unified Design System
   ═══════════════════════════════════════════════════════════ */

/* ── Light Theme (default) ──────────────────────────────── */
:root,
[data-theme="light"] {
  /* Brand */
  --flx-navy: #1E3A5F;
  --flx-navy-dark: #0c1f36;
  --flx-navy-darker: #0a1628;
  --flx-teal: #2EC4B6;
  --flx-teal-dark: #22A89D;
  --flx-teal-light: #5DD9CD;

  /* Semantic */
  --flx-success: #22C55E;
  --flx-success-light: #dcfce7;
  --flx-danger: #EF4444;
  --flx-danger-light: #fee2e2;
  --flx-warning: #F59E0B;
  --flx-warning-light: #fef3c7;
  --flx-info: #3B82F6;
  --flx-info-light: #dbeafe;

  /* Surfaces */
  --flx-bg: #f8fafc;
  --flx-surface: #ffffff;
  --flx-surface-2: #f1f5f9;
  --flx-border: #e2e8f0;
  --flx-text: #0f172a;
  --flx-text-secondary: #475569;
  --flx-text-muted: #94a3b8;

  /* Sidebar */
  --flx-sidebar-bg: #0f172a;
  --flx-sidebar-surface: #1e293b;
  --flx-sidebar-border: #334155;
  --flx-sidebar-text: #e2e8f0;
  --flx-sidebar-text-muted: #94a3b8;
  --flx-sidebar-active-bg: rgba(46, 196, 182, 0.15);
  --flx-sidebar-active-text: #2EC4B6;

  /* Header */
  --flx-header-bg: #ffffff;
  --flx-header-border: #e2e8f0;
  --flx-header-text: #0f172a;

  /* Layout */
  --flx-header-height: 60px;
  --flx-sidebar-width: 240px;
  --flx-sidebar-collapsed-width: 64px;
  --flx-bottom-nav-height: 64px;

  /* Typography */
  --flx-font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --flx-font-mono: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
  --flx-text-xs: 0.75rem;
  --flx-text-sm: 0.8125rem;
  --flx-text-base: 0.875rem;
  --flx-text-md: 1rem;
  --flx-text-lg: 1.125rem;
  --flx-text-xl: 1.375rem;
  --flx-text-2xl: 1.625rem;
  --flx-text-3xl: 2rem;

  /* Spacing */
  --flx-space-xs: 0.25rem;
  --flx-space-sm: 0.5rem;
  --flx-space-md: 0.75rem;
  --flx-space-lg: 1rem;
  --flx-space-xl: 1.5rem;
  --flx-space-2xl: 2rem;
  --flx-space-3xl: 3rem;

  /* Shadows */
  --flx-shadow-sm: 0 1px 3px rgba(0,0,0,0.08);
  --flx-shadow-md: 0 4px 12px rgba(0,0,0,0.1);
  --flx-shadow-lg: 0 8px 24px rgba(0,0,0,0.12);
  --flx-shadow-xl: 0 16px 48px rgba(0,0,0,0.16);

  /* Borders */
  --flx-radius-sm: 0.375rem;
  --flx-radius-md: 0.5rem;
  --flx-radius-lg: 0.75rem;
  --flx-radius-xl: 1rem;

  /* Transitions */
  --flx-transition-fast: 0.15s ease;
  --flx-transition-normal: 0.2s ease;
  --flx-transition-slow: 0.3s ease;
  --flx-transition-spring: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ── Dark Theme ─────────────────────────────────────────── */
[data-theme="dark"] {
  --flx-bg: #0f172a;
  --flx-surface: #1e293b;
  --flx-surface-2: #334155;
  --flx-border: #475569;
  --flx-text: #f1f5f9;
  --flx-text-secondary: #94a3b8;
  --flx-text-muted: #64748b;

  --flx-success-light: rgba(34, 197, 94, 0.15);
  --flx-danger-light: rgba(239, 68, 68, 0.15);
  --flx-warning-light: rgba(245, 158, 11, 0.15);
  --flx-info-light: rgba(59, 130, 246, 0.15);

  --flx-sidebar-bg: #0a1628;
  --flx-sidebar-surface: #0f172a;
  --flx-sidebar-border: #1e293b;

  --flx-header-bg: #1e293b;
  --flx-header-border: #334155;
  --flx-header-text: #f1f5f9;

  --flx-shadow-sm: 0 1px 3px rgba(0,0,0,0.3);
  --flx-shadow-md: 0 4px 12px rgba(0,0,0,0.4);
  --flx-shadow-lg: 0 8px 24px rgba(0,0,0,0.5);
  --flx-shadow-xl: 0 16px 48px rgba(0,0,0,0.6);
}
```

**Step 2: Commit**

```bash
git add packages/shared/src/theme/tokens.css
git commit -m "feat(design-system): add CSS design tokens with light/dark theme"
```

---

## Task 2: Create Theme Composable

**Files:**
- Create: `packages/shared/src/theme/useTheme.ts`
- Create: `packages/shared/src/theme/index.ts`

**Step 1: Create the useTheme composable**

```typescript
// packages/shared/src/theme/useTheme.ts
import { ref, watch, onMounted } from 'vue'

const STORAGE_KEY = 'flx-theme'

type Theme = 'light' | 'dark'

const theme = ref<Theme>('light')

function getSystemTheme(): Theme {
  if (typeof window === 'undefined') return 'light'
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}

function getStoredTheme(): Theme | null {
  if (typeof localStorage === 'undefined') return null
  const stored = localStorage.getItem(STORAGE_KEY)
  return stored === 'dark' || stored === 'light' ? stored : null
}

function applyTheme(t: Theme) {
  document.documentElement.setAttribute('data-theme', t)
  // Also toggle PrimeVue dark mode class
  if (t === 'dark') {
    document.documentElement.classList.add('dark')
  } else {
    document.documentElement.classList.remove('dark')
  }
}

export function useTheme() {
  function toggle() {
    theme.value = theme.value === 'light' ? 'dark' : 'light'
  }

  function setTheme(t: Theme) {
    theme.value = t
  }

  watch(theme, (val) => {
    applyTheme(val)
    localStorage.setItem(STORAGE_KEY, val)
  })

  onMounted(() => {
    const stored = getStoredTheme()
    theme.value = stored ?? getSystemTheme()
    applyTheme(theme.value)
  })

  return {
    theme,
    toggle,
    setTheme,
    isDark: () => theme.value === 'dark',
  }
}
```

**Step 2: Create barrel export**

```typescript
// packages/shared/src/theme/index.ts
export { useTheme } from './useTheme'
```

**Step 3: Commit**

```bash
git add packages/shared/src/theme/
git commit -m "feat(design-system): add useTheme composable with dark/light toggle"
```

---

## Task 3: Create Shared Vue Components — FlxSidebar

**Files:**
- Create: `packages/shared/src/components/FlxSidebar.vue`

**Step 1: Create FlxSidebar component**

This is the icon-first collapsible sidebar (64px collapsed → 240px expanded) used by all 3 apps.

```vue
<!-- packages/shared/src/components/FlxSidebar.vue -->
<script setup lang="ts">
import { ref, computed, useSlots } from 'vue'
import { useRoute } from 'vue-router'

export interface NavItem {
  path?: string
  label: string
  icon: any // Lucide component
  section?: string // section header label (renders as divider)
  children?: { label: string; path: string }[]
  badge?: string
}

const props = withDefaults(defineProps<{
  items: NavItem[]
  brandIcon?: string
  brandLabel?: string
  collapsed?: boolean
  mobileOpen?: boolean
}>(), {
  brandIcon: 'F',
  brandLabel: 'Monetarie',
  collapsed: false,
  mobileOpen: false,
})

const emit = defineEmits<{
  'update:collapsed': [value: boolean]
  'update:mobileOpen': [value: boolean]
  navigate: [path: string]
}>()

const route = useRoute()
const expandedGroups = ref<Set<string>>(new Set())

function isActive(path: string): boolean {
  if (path === '/dashboard') return route.path === '/dashboard'
  return route.path === path || route.path.startsWith(path + '/')
}

function toggleGroup(label: string) {
  if (expandedGroups.value.has(label)) {
    expandedGroups.value.delete(label)
  } else {
    expandedGroups.value.add(label)
  }
}

function handleNavigate(path: string) {
  emit('navigate', path)
  emit('update:mobileOpen', false)
}

function toggleCollapse() {
  emit('update:collapsed', !props.collapsed)
}
</script>

<template>
  <!-- Desktop sidebar -->
  <aside
    class="flx-sidebar"
    :class="{ collapsed, 'mobile-open': mobileOpen }"
  >
    <!-- Brand -->
    <div class="flx-sidebar-brand">
      <div class="brand-logo">
        <span>{{ brandIcon }}</span>
      </div>
      <span v-if="!collapsed" class="brand-text">{{ brandLabel }}</span>
    </div>

    <!-- Slot for custom header (merchant switcher, etc.) -->
    <slot name="header" />

    <!-- Navigation -->
    <nav class="flx-sidebar-nav">
      <template v-for="item in items" :key="item.label">
        <!-- Section header -->
        <div v-if="item.section" class="nav-section-label">
          <span v-if="!collapsed">{{ item.section }}</span>
          <div v-else class="nav-section-divider" />
        </div>

        <!-- Simple nav item -->
        <button
          v-else-if="item.path && !item.children"
          class="nav-item"
          :class="{ active: isActive(item.path) }"
          @click="handleNavigate(item.path)"
          :title="collapsed ? item.label : undefined"
        >
          <div class="nav-indicator" v-if="isActive(item.path)" />
          <div class="nav-icon-wrap">
            <component :is="item.icon" :size="20" />
          </div>
          <span v-if="!collapsed" class="nav-label">{{ item.label }}</span>
          <span v-if="!collapsed && item.badge" class="nav-badge">{{ item.badge }}</span>
        </button>

        <!-- Group with children -->
        <div v-else-if="item.children" class="nav-group">
          <button
            class="nav-item"
            :class="{ active: item.children.some(c => isActive(c.path)) }"
            @click="collapsed ? undefined : toggleGroup(item.label)"
            :title="collapsed ? item.label : undefined"
          >
            <div class="nav-indicator" v-if="item.children.some(c => isActive(c.path))" />
            <div class="nav-icon-wrap">
              <component :is="item.icon" :size="20" />
            </div>
            <span v-if="!collapsed" class="nav-label">{{ item.label }}</span>
            <svg
              v-if="!collapsed"
              class="nav-chevron"
              :class="{ rotated: expandedGroups.has(item.label) }"
              width="16" height="16" viewBox="0 0 24 24"
              fill="none" stroke="currentColor" stroke-width="2"
            >
              <polyline points="6 9 12 15 18 9" />
            </svg>
          </button>
          <div
            v-if="!collapsed && expandedGroups.has(item.label)"
            class="nav-submenu"
          >
            <button
              v-for="child in item.children"
              :key="child.path"
              class="nav-subitem"
              :class="{ active: isActive(child.path) }"
              @click="handleNavigate(child.path)"
            >
              {{ child.label }}
            </button>
          </div>
        </div>
      </template>
    </nav>

    <!-- Footer -->
    <div class="flx-sidebar-footer">
      <slot name="footer" />
      <button class="collapse-btn" @click="toggleCollapse">
        <svg
          :class="{ rotated: collapsed }"
          width="20" height="20" viewBox="0 0 24 24"
          fill="none" stroke="currentColor" stroke-width="2"
        >
          <polyline points="15 18 9 12 15 6" />
        </svg>
        <span v-if="!collapsed">Recolher</span>
      </button>
    </div>
  </aside>

  <!-- Mobile overlay -->
  <Teleport to="body">
    <div
      v-if="mobileOpen"
      class="flx-sidebar-overlay"
      @click="$emit('update:mobileOpen', false)"
    />
  </Teleport>
</template>

<style scoped>
.flx-sidebar {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  width: var(--flx-sidebar-width);
  background: var(--flx-sidebar-bg);
  color: var(--flx-sidebar-text);
  display: flex;
  flex-direction: column;
  z-index: 50;
  transition: width var(--flx-transition-normal), transform var(--flx-transition-normal);
  overflow: hidden;
}

.flx-sidebar.collapsed {
  width: var(--flx-sidebar-collapsed-width);
}

/* Brand */
.flx-sidebar-brand {
  display: flex;
  align-items: center;
  gap: var(--flx-space-sm);
  padding: var(--flx-space-lg);
  border-bottom: 1px solid var(--flx-sidebar-border);
  min-height: var(--flx-header-height);
}

.brand-logo {
  width: 32px;
  height: 32px;
  border-radius: var(--flx-radius-md);
  background: linear-gradient(135deg, var(--flx-teal), var(--flx-teal-dark));
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: 0 2px 8px rgba(46, 196, 182, 0.3);
}

.brand-logo span {
  font-size: var(--flx-text-base);
  font-weight: 800;
  color: white;
}

.brand-text {
  font-size: var(--flx-text-lg);
  font-weight: 700;
  color: var(--flx-sidebar-text);
  white-space: nowrap;
}

/* Navigation */
.flx-sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: var(--flx-space-sm);
}

.nav-section-label {
  padding: var(--flx-space-lg) var(--flx-space-md) var(--flx-space-xs);
}

.nav-section-label span {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--flx-sidebar-text-muted);
}

.nav-section-divider {
  height: 1px;
  background: var(--flx-sidebar-border);
  margin: var(--flx-space-sm) var(--flx-space-md);
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--flx-space-md);
  width: 100%;
  padding: var(--flx-space-sm) var(--flx-space-md);
  border: none;
  border-radius: var(--flx-radius-md);
  background: none;
  color: var(--flx-sidebar-text-muted);
  font-size: var(--flx-text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: all var(--flx-transition-fast);
  text-align: left;
}

.nav-item:hover {
  background: var(--flx-sidebar-surface);
  color: var(--flx-sidebar-text);
}

.nav-item.active {
  background: var(--flx-sidebar-active-bg);
  color: var(--flx-sidebar-active-text);
  font-weight: 600;
}

.nav-indicator {
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 3px;
  border-radius: 0 2px 2px 0;
  background: var(--flx-teal);
}

.nav-icon-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
}

.nav-label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}

.nav-badge {
  font-size: 0.625rem;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--flx-teal);
  color: white;
}

.nav-chevron {
  margin-left: auto;
  transition: transform var(--flx-transition-fast);
  flex-shrink: 0;
  opacity: 0.5;
}

.nav-chevron.rotated {
  transform: rotate(180deg);
}

.nav-submenu {
  padding-left: var(--flx-space-lg);
}

.nav-subitem {
  width: 100%;
  padding: 6px var(--flx-space-md) 6px calc(var(--flx-space-lg) + var(--flx-space-md));
  border: none;
  border-radius: var(--flx-radius-sm);
  background: none;
  color: var(--flx-sidebar-text-muted);
  font-size: var(--flx-text-sm);
  cursor: pointer;
  text-align: left;
  transition: all var(--flx-transition-fast);
}

.nav-subitem:hover {
  background: var(--flx-sidebar-surface);
  color: var(--flx-sidebar-text);
}

.nav-subitem.active {
  color: var(--flx-sidebar-active-text);
  background: rgba(46, 196, 182, 0.08);
}

/* Footer */
.flx-sidebar-footer {
  padding: var(--flx-space-sm);
  border-top: 1px solid var(--flx-sidebar-border);
}

.collapse-btn {
  display: flex;
  align-items: center;
  gap: var(--flx-space-md);
  width: 100%;
  padding: var(--flx-space-sm) var(--flx-space-md);
  border: none;
  border-radius: var(--flx-radius-md);
  background: none;
  color: var(--flx-sidebar-text-muted);
  font-size: var(--flx-text-sm);
  cursor: pointer;
  transition: all var(--flx-transition-fast);
}

.collapse-btn:hover {
  background: var(--flx-sidebar-surface);
  color: var(--flx-sidebar-text);
}

.collapse-btn svg {
  transition: transform var(--flx-transition-fast);
  flex-shrink: 0;
}

.collapse-btn svg.rotated {
  transform: rotate(180deg);
}

/* Overlay */
.flx-sidebar-overlay {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
}

/* Mobile */
@media (max-width: 768px) {
  .flx-sidebar {
    transform: translateX(-100%);
    width: var(--flx-sidebar-width);
  }

  .flx-sidebar.mobile-open {
    transform: translateX(0);
    z-index: 60;
  }

  .flx-sidebar.collapsed {
    width: var(--flx-sidebar-width);
  }
}
</style>
```

**Step 2: Commit**

```bash
git add packages/shared/src/components/FlxSidebar.vue
git commit -m "feat(design-system): add FlxSidebar component"
```

---

## Task 4: Create Shared Vue Components — FlxHeader

**Files:**
- Create: `packages/shared/src/components/FlxHeader.vue`

**Step 1: Create FlxHeader component**

```vue
<!-- packages/shared/src/components/FlxHeader.vue -->
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'

const props = withDefaults(defineProps<{
  sidebarCollapsed?: boolean
  balanceVisible?: boolean
  balance?: number | null
  userName?: string
  userInitial?: string
  theme?: 'light' | 'dark'
  showBalance?: boolean
  showThemeToggle?: boolean
}>(), {
  sidebarCollapsed: false,
  balanceVisible: false,
  balance: null,
  userName: '',
  userInitial: 'U',
  theme: 'light',
  showBalance: true,
  showThemeToggle: true,
})

const emit = defineEmits<{
  toggleSidebar: []
  toggleMobile: []
  toggleBalance: []
  toggleTheme: []
  logout: []
}>()

const route = useRoute()

const breadcrumbs = computed(() => {
  const parts = route.path.split('/').filter(Boolean)
  return parts.map((part, index) => ({
    label: part.charAt(0).toUpperCase() + part.slice(1).replace(/-/g, ' '),
    path: '/' + parts.slice(0, index + 1).join('/'),
    isLast: index === parts.length - 1,
  }))
})

function formatBalance(value: number): string {
  return new Intl.NumberFormat('pt-BR', {
    style: 'currency',
    currency: 'BRL',
  }).format(value / 100)
}
</script>

<template>
  <header class="flx-header" :class="{ 'sidebar-collapsed': sidebarCollapsed }">
    <div class="header-left">
      <!-- Desktop sidebar toggle -->
      <button class="header-icon-btn desktop-only" @click="$emit('toggleSidebar')" aria-label="Toggle sidebar">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" />
        </svg>
      </button>

      <!-- Mobile menu toggle -->
      <button class="header-icon-btn mobile-only" @click="$emit('toggleMobile')" aria-label="Menu">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" />
        </svg>
      </button>

      <!-- Breadcrumbs -->
      <nav class="breadcrumbs desktop-only" aria-label="Breadcrumb">
        <template v-for="(crumb, i) in breadcrumbs" :key="crumb.path">
          <span v-if="i > 0" class="breadcrumb-separator">/</span>
          <span
            class="breadcrumb-item"
            :class="{ current: crumb.isLast }"
          >{{ crumb.label }}</span>
        </template>
      </nav>
    </div>

    <div class="header-right">
      <!-- Slot for custom content (merchant switcher, etc.) -->
      <slot name="center" />

      <!-- Balance display -->
      <div v-if="showBalance && balance !== null" class="balance-display">
        <span class="balance-value">
          {{ balanceVisible ? formatBalance(balance) : 'R$ ••••••' }}
        </span>
        <button class="header-icon-btn" @click="$emit('toggleBalance')" aria-label="Toggle balance visibility">
          <svg v-if="balanceVisible" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" /><circle cx="12" cy="12" r="3" />
          </svg>
          <svg v-else width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
            <line x1="1" y1="1" x2="23" y2="23" />
          </svg>
        </button>
      </div>

      <!-- Theme toggle -->
      <button v-if="showThemeToggle" class="header-icon-btn" @click="$emit('toggleTheme')" aria-label="Toggle theme">
        <svg v-if="theme === 'light'" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
        </svg>
        <svg v-else width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <circle cx="12" cy="12" r="5" /><line x1="12" y1="1" x2="12" y2="3" /><line x1="12" y1="21" x2="12" y2="23" /><line x1="4.22" y1="4.22" x2="5.64" y2="5.64" /><line x1="18.36" y1="18.36" x2="19.78" y2="19.78" /><line x1="1" y1="12" x2="3" y2="12" /><line x1="21" y1="12" x2="23" y2="12" /><line x1="4.22" y1="19.78" x2="5.64" y2="18.36" /><line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
        </svg>
      </button>

      <!-- Slot for right-side items (profile menu, etc.) -->
      <slot name="right" />
    </div>
  </header>
</template>

<style scoped>
.flx-header {
  position: sticky;
  top: 0;
  z-index: 40;
  height: var(--flx-header-height);
  background: var(--flx-header-bg);
  border-bottom: 1px solid var(--flx-header-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--flx-space-xl);
  transition: margin-left var(--flx-transition-normal);
}

.header-left,
.header-right {
  display: flex;
  align-items: center;
  gap: var(--flx-space-sm);
}

.header-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: var(--flx-radius-md);
  background: none;
  color: var(--flx-text-secondary);
  cursor: pointer;
  transition: all var(--flx-transition-fast);
}

.header-icon-btn:hover {
  background: var(--flx-surface-2);
  color: var(--flx-text);
}

/* Breadcrumbs */
.breadcrumbs {
  display: flex;
  align-items: center;
  gap: var(--flx-space-xs);
  margin-left: var(--flx-space-sm);
}

.breadcrumb-separator {
  color: var(--flx-text-muted);
  font-size: var(--flx-text-sm);
}

.breadcrumb-item {
  font-size: var(--flx-text-sm);
  color: var(--flx-text-secondary);
}

.breadcrumb-item.current {
  color: var(--flx-text);
  font-weight: 600;
}

/* Balance */
.balance-display {
  display: flex;
  align-items: center;
  gap: var(--flx-space-xs);
  padding: var(--flx-space-xs) var(--flx-space-sm);
  border-radius: var(--flx-radius-md);
  background: var(--flx-surface-2);
}

.balance-value {
  font-size: var(--flx-text-sm);
  font-weight: 600;
  color: var(--flx-text);
  font-family: var(--flx-font-mono);
}

/* Responsive */
.mobile-only { display: none; }

@media (max-width: 768px) {
  .desktop-only { display: none !important; }
  .mobile-only { display: flex; }

  .flx-header {
    padding: 0 var(--flx-space-lg);
  }
}
</style>
```

**Step 2: Commit**

```bash
git add packages/shared/src/components/FlxHeader.vue
git commit -m "feat(design-system): add FlxHeader component with breadcrumbs and balance"
```

---

## Task 5: Create Shared Components — FlxFinancialCard, FlxQuickAction, FlxDrawer, FlxBottomNav

**Files:**
- Create: `packages/shared/src/components/FlxFinancialCard.vue`
- Create: `packages/shared/src/components/FlxQuickAction.vue`
- Create: `packages/shared/src/components/FlxDrawer.vue`
- Create: `packages/shared/src/components/FlxBottomNav.vue`
- Create: `packages/shared/src/components/index.ts`

**Step 1: Create FlxFinancialCard**

```vue
<!-- packages/shared/src/components/FlxFinancialCard.vue -->
<script setup lang="ts">
const props = withDefaults(defineProps<{
  type: 'income' | 'balance' | 'expense'
  label: string
  value: number
  visible?: boolean
}>(), {
  visible: false,
})

const colorMap = {
  income: { accent: 'var(--flx-success)', bg: 'var(--flx-success-light)' },
  balance: { accent: 'var(--flx-info)', bg: 'var(--flx-info-light)' },
  expense: { accent: 'var(--flx-danger)', bg: 'var(--flx-danger-light)' },
}

function formatCurrency(value: number): string {
  return new Intl.NumberFormat('pt-BR', {
    style: 'currency',
    currency: 'BRL',
  }).format(value / 100)
}
</script>

<template>
  <div class="flx-financial-card" :class="type">
    <div class="card-accent" :style="{ background: colorMap[type].accent }" />
    <div class="card-body">
      <span class="card-label">{{ label }}</span>
      <span class="card-value">
        {{ visible ? formatCurrency(value) : 'R$ ••••••' }}
      </span>
    </div>
    <div class="card-icon" :style="{ background: colorMap[type].bg }">
      <svg v-if="type === 'income'" width="20" height="20" viewBox="0 0 24 24" fill="none" :stroke="colorMap[type].accent" stroke-width="2">
        <line x1="12" y1="19" x2="12" y2="5" /><polyline points="5 12 12 5 19 12" />
      </svg>
      <svg v-else-if="type === 'balance'" width="20" height="20" viewBox="0 0 24 24" fill="none" :stroke="colorMap[type].accent" stroke-width="2">
        <rect x="2" y="4" width="20" height="16" rx="2" /><line x1="2" y1="10" x2="22" y2="10" />
      </svg>
      <svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" :stroke="colorMap[type].accent" stroke-width="2">
        <line x1="12" y1="5" x2="12" y2="19" /><polyline points="19 12 12 19 5 12" />
      </svg>
    </div>
  </div>
</template>

<style scoped>
.flx-financial-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--flx-space-lg);
  padding: var(--flx-space-xl);
  background: var(--flx-surface);
  border-radius: var(--flx-radius-lg);
  box-shadow: var(--flx-shadow-sm);
  overflow: hidden;
  transition: transform var(--flx-transition-fast), box-shadow var(--flx-transition-fast);
  cursor: default;
}

.flx-financial-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--flx-shadow-md);
}

.card-accent {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 4px;
}

.card-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--flx-space-xs);
}

.card-label {
  font-size: var(--flx-text-sm);
  color: var(--flx-text-secondary);
  font-weight: 500;
}

.card-value {
  font-size: var(--flx-text-2xl);
  font-weight: 700;
  color: var(--flx-text);
  font-family: var(--flx-font-family);
}

.card-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--flx-radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
</style>
```

**Step 2: Create FlxQuickAction**

```vue
<!-- packages/shared/src/components/FlxQuickAction.vue -->
<script setup lang="ts">
const props = withDefaults(defineProps<{
  icon: any
  label: string
  to?: string
  badge?: string
  disabled?: boolean
}>(), {
  disabled: false,
})

const emit = defineEmits<{
  click: []
}>()
</script>

<template>
  <router-link
    v-if="to && !disabled"
    :to="to"
    class="flx-quick-action"
  >
    <div class="action-icon">
      <component :is="icon" :size="24" />
    </div>
    <span class="action-label">{{ label }}</span>
    <span v-if="badge" class="action-badge">{{ badge }}</span>
  </router-link>
  <button
    v-else
    class="flx-quick-action"
    :class="{ disabled }"
    @click="!disabled && $emit('click')"
  >
    <div class="action-icon">
      <component :is="icon" :size="24" />
    </div>
    <span class="action-label">{{ label }}</span>
    <span v-if="badge" class="action-badge">{{ badge }}</span>
  </button>
</template>

<style scoped>
.flx-quick-action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--flx-space-sm);
  padding: var(--flx-space-lg);
  border: 1px solid var(--flx-border);
  border-radius: var(--flx-radius-lg);
  background: var(--flx-surface);
  cursor: pointer;
  transition: all var(--flx-transition-fast);
  text-decoration: none;
  color: var(--flx-text);
  position: relative;
}

.flx-quick-action:hover {
  border-color: var(--flx-teal);
  box-shadow: var(--flx-shadow-sm);
  transform: translateY(-1px);
}

.flx-quick-action.disabled {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}

.action-icon {
  width: 48px;
  height: 48px;
  border-radius: var(--flx-radius-md);
  background: var(--flx-surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--flx-teal);
}

.action-label {
  font-size: var(--flx-text-sm);
  font-weight: 500;
  text-align: center;
  line-height: 1.2;
}

.action-badge {
  position: absolute;
  top: var(--flx-space-sm);
  right: var(--flx-space-sm);
  font-size: 0.625rem;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--flx-teal);
  color: white;
}
</style>
```

**Step 3: Create FlxDrawer**

```vue
<!-- packages/shared/src/components/FlxDrawer.vue -->
<script setup lang="ts">
const props = withDefaults(defineProps<{
  visible: boolean
  title: string
  subtitle?: string
  width?: string
}>(), {
  width: '400px',
})

const emit = defineEmits<{
  'update:visible': [value: boolean]
}>()

function close() {
  emit('update:visible', false)
}
</script>

<template>
  <Teleport to="body">
    <Transition name="drawer-overlay">
      <div v-if="visible" class="flx-drawer-overlay" @click="close" />
    </Transition>
    <Transition name="drawer-panel">
      <div v-if="visible" class="flx-drawer" :style="{ width }">
        <div class="drawer-header">
          <div>
            <h3 class="drawer-title">{{ title }}</h3>
            <p v-if="subtitle" class="drawer-subtitle">{{ subtitle }}</p>
          </div>
          <button class="drawer-close" @click="close" aria-label="Fechar">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" />
            </svg>
          </button>
        </div>
        <div class="drawer-body">
          <slot />
        </div>
        <div class="drawer-footer" v-if="$slots.footer">
          <slot name="footer" />
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.flx-drawer-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
}

.flx-drawer {
  position: fixed;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 101;
  background: var(--flx-surface);
  box-shadow: var(--flx-shadow-xl);
  display: flex;
  flex-direction: column;
  max-width: 100vw;
}

.drawer-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: var(--flx-space-xl);
  border-bottom: 1px solid var(--flx-border);
}

.drawer-title {
  font-size: var(--flx-text-lg);
  font-weight: 700;
  color: var(--flx-text);
  margin: 0;
}

.drawer-subtitle {
  font-size: var(--flx-text-sm);
  color: var(--flx-text-secondary);
  margin: var(--flx-space-xs) 0 0;
}

.drawer-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: var(--flx-radius-md);
  background: none;
  color: var(--flx-text-muted);
  cursor: pointer;
  transition: all var(--flx-transition-fast);
}

.drawer-close:hover {
  background: var(--flx-surface-2);
  color: var(--flx-text);
}

.drawer-body {
  flex: 1;
  overflow-y: auto;
  padding: var(--flx-space-xl);
}

.drawer-footer {
  padding: var(--flx-space-xl);
  border-top: 1px solid var(--flx-border);
}

/* Transitions */
.drawer-overlay-enter-active,
.drawer-overlay-leave-active {
  transition: opacity var(--flx-transition-slow);
}
.drawer-overlay-enter-from,
.drawer-overlay-leave-to {
  opacity: 0;
}

.drawer-panel-enter-active {
  transition: transform var(--flx-transition-slow);
}
.drawer-panel-leave-active {
  transition: transform var(--flx-transition-normal);
}
.drawer-panel-enter-from,
.drawer-panel-leave-to {
  transform: translateX(100%);
}
</style>
```

**Step 4: Create FlxBottomNav**

```vue
<!-- packages/shared/src/components/FlxBottomNav.vue -->
<script setup lang="ts">
import { useRoute } from 'vue-router'

export interface BottomNavItem {
  path: string
  label: string
  icon: any
}

defineProps<{
  items: BottomNavItem[]
}>()

const emit = defineEmits<{
  navigate: [path: string]
}>()

const route = useRoute()

function isActive(path: string): boolean {
  return route.path === path || route.path.startsWith(path + '/')
}
</script>

<template>
  <nav class="flx-bottom-nav">
    <button
      v-for="item in items"
      :key="item.path"
      class="bottom-nav-item"
      :class="{ active: isActive(item.path) }"
      @click="$emit('navigate', item.path)"
    >
      <component :is="item.icon" :size="22" />
      <span>{{ item.label }}</span>
    </button>
  </nav>
</template>

<style scoped>
.flx-bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 50;
  height: var(--flx-bottom-nav-height);
  background: var(--flx-surface);
  border-top: 1px solid var(--flx-border);
  display: none;
  align-items: center;
  justify-content: space-around;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
}

.bottom-nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: var(--flx-space-xs) var(--flx-space-md);
  border: none;
  background: none;
  color: var(--flx-text-muted);
  font-size: 0.625rem;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--flx-transition-fast);
  position: relative;
  text-decoration: none;
}

.bottom-nav-item.active {
  color: var(--flx-teal);
}

.bottom-nav-item.active::before {
  content: '';
  position: absolute;
  top: -1px;
  left: 25%;
  right: 25%;
  height: 2px;
  background: var(--flx-teal);
  border-radius: 0 0 2px 2px;
}

.bottom-nav-item:hover:not(.active) {
  color: var(--flx-text-secondary);
}

@media (max-width: 768px) {
  .flx-bottom-nav {
    display: flex;
  }
}
</style>
```

**Step 5: Create component barrel export**

```typescript
// packages/shared/src/components/index.ts
export { default as FlxSidebar } from './FlxSidebar.vue'
export { default as FlxHeader } from './FlxHeader.vue'
export { default as FlxFinancialCard } from './FlxFinancialCard.vue'
export { default as FlxQuickAction } from './FlxQuickAction.vue'
export { default as FlxDrawer } from './FlxDrawer.vue'
export { default as FlxBottomNav } from './FlxBottomNav.vue'
```

**Step 6: Commit**

```bash
git add packages/shared/src/components/
git commit -m "feat(design-system): add FlxFinancialCard, FlxQuickAction, FlxDrawer, FlxBottomNav components"
```

---

## Task 6: Update @monetarie/shared Package Exports

**Files:**
- Modify: `packages/shared/package.json`
- Modify: `packages/shared/tsup.config.ts`
- Modify: `packages/shared/src/index.ts`

**Step 1: Update package.json — add new exports**

Add these entries to the `"exports"` object in `packages/shared/package.json`:

```json
"./theme": {
  "import": "./src/theme/index.ts",
  "types": "./src/theme/index.ts"
},
"./theme/tokens.css": {
  "import": "./src/theme/tokens.css"
},
"./components": {
  "import": "./src/components/index.ts",
  "types": "./src/components/index.ts"
}
```

Also add `vue` and `vue-router` as peerDependencies:

```json
"peerDependencies": {
  "vue": "^3.5.0",
  "vue-router": "^4.5.0"
}
```

**Step 2: Update barrel export**

Add to `packages/shared/src/index.ts`:

```typescript
export { useTheme } from './theme'
export * from './components'
```

**Step 3: Commit**

```bash
git add packages/shared/package.json packages/shared/tsup.config.ts packages/shared/src/index.ts
git commit -m "feat(design-system): export theme and components from @monetarie/shared"
```

---

## Task 7: Integrate Design System into Banking App

**Files:**
- Modify: `apps/banking/src/main.ts` — import tokens.css
- Rewrite: `apps/banking/src/layouts/AuthenticatedLayout.vue` — use FlxSidebar + FlxHeader

**Step 1: Add tokens import to main.ts**

At the top of `apps/banking/src/main.ts`, add:

```typescript
import '@monetarie/shared/theme/tokens.css'
```

**Step 2: Rewrite AuthenticatedLayout.vue**

Replace the entire `AuthenticatedLayout.vue` with the new design using `FlxSidebar`, `FlxHeader`, and `FlxBottomNav` from `@monetarie/shared/components`. The layout should:

- Import all shared components
- Use the `useTheme` composable for dark/light toggle
- Keep existing auth store, account store, and i18n integrations
- Map existing nav items to FlxSidebar format using Lucide icons
- Include balance masking via FlxHeader
- Include FlxBottomNav for mobile
- Use `useAccountStore` for balance data

Key navigation items to map:
- Main: Dashboard, PIX, Transfer, Payments, Statement, Receipts
- Secondary: Security, API Keys, Settings
- New: Analytics, MEDs, Fees, Limits, Ledger, Transaction Search

**Step 3: Commit**

```bash
git add apps/banking/src/main.ts apps/banking/src/layouts/AuthenticatedLayout.vue
git commit -m "feat(banking): integrate unified design system layout"
```

---

## Task 8: Integrate Design System into Merchant App

**Files:**
- Modify: `apps/merchant/src/main.ts` — import tokens.css
- Rewrite: `apps/merchant/src/layouts/MerchantLayout.vue` — use FlxSidebar + FlxHeader

**Step 1: Add tokens import to main.ts**

```typescript
import '@monetarie/shared/theme/tokens.css'
```

**Step 2: Rewrite MerchantLayout.vue**

Replace with new design using shared components. Keep:
- Merchant switcher in sidebar header slot
- Balance display in header
- Profile menu in header right slot
- All existing nav items mapped to Lucide icons
- Add new nav items: Analytics, MEDs, Fees, Limits, Ledger

**Step 3: Commit**

```bash
git add apps/merchant/src/main.ts apps/merchant/src/layouts/MerchantLayout.vue
git commit -m "feat(merchant): integrate unified design system layout"
```

---

## Task 9: Integrate Design System into Admin App

**Files:**
- Modify: `apps/admin/src/main.ts` — import tokens.css
- Rewrite: `apps/admin/src/layouts/AdminLayout.vue` — use FlxSidebar + FlxHeader

**Step 1: Add tokens import**

```typescript
import '@monetarie/shared/theme/tokens.css'
```

**Step 2: Rewrite AdminLayout.vue**

Replace with new design using shared components. Keep:
- Merchant switcher (PrimeVue Select) in header center slot
- Profile menu in header right slot
- All existing menu items with section headers mapped to FlxSidebar

**Step 3: Commit**

```bash
git add apps/admin/src/main.ts apps/admin/src/layouts/AdminLayout.vue
git commit -m "feat(admin): integrate unified design system layout"
```

---

## Task 10: Add New Banking Routes & Views — Dashboard Analytics

**Files:**
- Create: `apps/banking/src/views/DashboardAnalyticsView.vue`
- Modify: `apps/banking/src/router/index.ts` — add route

**Step 1: Create DashboardAnalyticsView**

Dashboard with:
- 4 summary cards (Entradas, Saidas, Estornos, Resultado)
- Line chart (30d trend)
- Bar chart (by channel: PIX, TED, Boleto)
- QR code conversion rate gauge
- Date range filter

Uses Chart.js via `vue-chartjs` (already a dependency in merchant, add to banking if needed).

**Step 2: Add route**

Add to `apps/banking/src/router/index.ts` under authenticated routes:

```typescript
{
  path: '/dashboard/analytics',
  name: 'DashboardAnalytics',
  component: () => import('@/views/DashboardAnalyticsView.vue'),
  meta: { requiresAuth: true },
},
```

**Step 3: Commit**

```bash
git add apps/banking/src/views/DashboardAnalyticsView.vue apps/banking/src/router/index.ts
git commit -m "feat(banking): add analytics dashboard view"
```

---

## Task 11: Add New Banking Views — MEDs, Fees, Limits

**Files:**
- Create: `apps/banking/src/views/MedsView.vue`
- Create: `apps/banking/src/views/FeesView.vue`
- Create: `apps/banking/src/views/LimitsView.vue`
- Modify: `apps/banking/src/router/index.ts` — add 3 routes

**Step 1: Create MedsView**

Page showing:
- 4 status cards (Total, Aguardando Defesa, Analise Juridica, Fechado)
- DataTable of MEDs with status, amount, date, E2E ID
- Empty state with explanation of what MEDs are

**Step 2: Create FeesView**

Table showing fees by channel:
- PIX Recebido: % + fixed fee
- QR Code: % + fixed fee
- PIX Enviado: fee
- TED: fee
- Read-only, informational

**Step 3: Create LimitsView**

Table showing limits by channel:
- Columns: Canal, Diario, Diurno, Noturno, Por Transacao
- Rows: PIX Enviado, PIX QR Code, TED, Saque
- Horario diurno/noturno info
- Special rules section

**Step 4: Add routes**

```typescript
{ path: '/meds', name: 'Meds', component: () => import('@/views/MedsView.vue'), meta: { requiresAuth: true } },
{ path: '/fees', name: 'Fees', component: () => import('@/views/FeesView.vue'), meta: { requiresAuth: true } },
{ path: '/limits', name: 'Limits', component: () => import('@/views/LimitsView.vue'), meta: { requiresAuth: true } },
```

**Step 5: Commit**

```bash
git add apps/banking/src/views/MedsView.vue apps/banking/src/views/FeesView.vue apps/banking/src/views/LimitsView.vue apps/banking/src/router/index.ts
git commit -m "feat(banking): add MEDs, Fees, and Limits views"
```

---

## Task 12: Add New Banking Views — Ledger, Transaction Search

**Files:**
- Create: `apps/banking/src/views/LedgerView.vue`
- Create: `apps/banking/src/views/TransactionSearchView.vue`
- Modify: `apps/banking/src/router/index.ts` — add 2 routes

**Step 1: Create LedgerView**

Full ledger table with columns:
- E2E ID, Tipo, Motivo, Status, Valor Bruto, Taxa, Valor Liquido, Data Criacao, Data Liquidacao
- Filters: date range, type, status
- Striped rows, status badges

**Step 2: Create TransactionSearchView**

Single search field that accepts:
- E2E ID
- Entry ID
- External ID
Auto-detects format and shows expanded transaction details.

**Step 3: Add routes**

```typescript
{ path: '/ledger', name: 'Ledger', component: () => import('@/views/LedgerView.vue'), meta: { requiresAuth: true } },
{ path: '/transaction-search', name: 'TransactionSearch', component: () => import('@/views/TransactionSearchView.vue'), meta: { requiresAuth: true } },
```

**Step 4: Commit**

```bash
git add apps/banking/src/views/LedgerView.vue apps/banking/src/views/TransactionSearchView.vue apps/banking/src/router/index.ts
git commit -m "feat(banking): add Ledger and Transaction Search views"
```

---

## Task 13: Redesign Banking DashboardView with Quick Access + Financial Cards

**Files:**
- Modify: `apps/banking/src/views/DashboardView.vue`

**Step 1: Rewrite DashboardView**

Replace current dashboard with:
1. Welcome header (greeting + account info)
2. 3 FlxFinancialCard components (income, balance, expense)
3. Quick Access grid using FlxQuickAction (18 items from design doc)
4. Recent Transactions table (compact, last 5)
5. Drawer integration for Receber PIX and Pagar QR Code

**Step 2: Commit**

```bash
git add apps/banking/src/views/DashboardView.vue
git commit -m "feat(banking): redesign dashboard with financial cards and quick access"
```

---

## Task 14: Add New Merchant Views — Analytics, MEDs, Fees, Limits, Ledger, Transaction Search

**Files:**
- Create: `apps/merchant/src/views/DashboardAnalyticsView.vue`
- Create: `apps/merchant/src/views/MedsView.vue`
- Create: `apps/merchant/src/views/FeesView.vue`
- Create: `apps/merchant/src/views/LimitsView.vue`
- Create: `apps/merchant/src/views/LedgerView.vue`
- Create: `apps/merchant/src/views/TransactionSearchView.vue`
- Modify: `apps/merchant/src/router/index.ts` — add 6 routes

Same structure as banking views but adapted for merchant context (CNPJ instead of CPF, merchant-specific API endpoints).

**Step 1: Create all 6 views**

Mirror banking views with merchant-specific adaptations.

**Step 2: Add routes under `/dashboard/`**

```typescript
{ path: 'analytics', name: 'MerchantAnalytics', component: () => import('@/views/DashboardAnalyticsView.vue') },
{ path: 'meds', name: 'MerchantMeds', component: () => import('@/views/MedsView.vue') },
{ path: 'fees', name: 'MerchantFees', component: () => import('@/views/FeesView.vue') },
{ path: 'limits', name: 'MerchantLimits', component: () => import('@/views/LimitsView.vue') },
{ path: 'ledger', name: 'MerchantLedger', component: () => import('@/views/LedgerView.vue') },
{ path: 'transaction-search', name: 'MerchantTransactionSearch', component: () => import('@/views/TransactionSearchView.vue') },
```

**Step 3: Commit**

```bash
git add apps/merchant/src/views/ apps/merchant/src/router/index.ts
git commit -m "feat(merchant): add analytics, MEDs, fees, limits, ledger, transaction search views"
```

---

## Task 15: Redesign Merchant DashboardView

**Files:**
- Modify: `apps/merchant/src/views/DashboardView.vue`

**Step 1: Rewrite DashboardView**

Replace with:
1. Company header (name + CNPJ + account)
2. 3 FlxFinancialCard components
3. Quick Access grid (merchant-specific: API Keys, Webhooks, Virtual Accounts, Documents, etc.)
4. Recent Transactions table

**Step 2: Commit**

```bash
git add apps/merchant/src/views/DashboardView.vue
git commit -m "feat(merchant): redesign dashboard with financial cards and quick access"
```

---

## Task 16: Enhance Admin Dashboard with Real Charts

**Files:**
- Modify: `apps/admin/src/views/DashboardView.vue`

**Step 1: Enhance DashboardView**

Add to existing stat cards:
- Line chart: Transaction volume (30d trend) using Chart.js
- Bar chart: Revenue by channel (PIX In, PIX Out, TED)
- Donut chart: QR code conversion rate
- Alert cards: Pending MEDs, Pending KYC, Failed webhooks
- Keep existing gradient stat cards and activity timeline

**Step 2: Commit**

```bash
git add apps/admin/src/views/DashboardView.vue
git commit -m "feat(admin): enhance dashboard with real charts and alerts"
```

---

## Task 17: Add i18n Keys for New Pages

**Files:**
- Modify: `packages/shared/src/i18n/locales/pt-BR.json`
- Modify: `packages/shared/src/i18n/locales/en.json`
- Modify: `packages/shared/src/i18n/locales/zh-CN.json`

**Step 1: Add translation keys for new pages**

Keys needed:
- `nav.analytics`, `nav.meds`, `nav.fees`, `nav.limits`, `nav.ledger`, `nav.transactionSearch`
- `dashboard.welcome`, `dashboard.quickAccess`, `dashboard.recentTransactions`
- `meds.title`, `meds.total`, `meds.pending`, `meds.legal`, `meds.closed`
- `fees.title`, `fees.channel`, `fees.percentage`, `fees.fixed`, `fees.volume`
- `limits.title`, `limits.daily`, `limits.daytime`, `limits.nighttime`, `limits.perTransaction`
- `ledger.title`, `ledger.gross`, `ledger.fee`, `ledger.net`
- `theme.light`, `theme.dark`

**Step 2: Commit**

```bash
git add packages/shared/src/i18n/locales/
git commit -m "feat(i18n): add translation keys for redesign pages"
```

---

## Task 18: Build Shared Package and Verify

**Step 1: Build shared package**

```bash
cd /monetarie/core && npx pnpm --filter @monetarie/shared build
```

**Step 2: Verify banking app builds**

```bash
cd /monetarie/core && npx pnpm --filter @monetarie/banking build
```

**Step 3: Verify merchant app builds**

```bash
cd /monetarie/core && npx pnpm --filter @monetarie/merchant-portal build
```

**Step 4: Verify admin app builds**

```bash
cd /monetarie/core && npx pnpm --filter @monetarie/admin build
```

**Step 5: Commit any build fixes**

```bash
git add -A && git commit -m "fix: resolve build issues from redesign integration"
```

---

## Task 19: Final Commit and Summary

**Step 1: Create summary commit if needed**

Review all changes, ensure consistency across apps.

**Step 2: Update MEMORY.md with new architecture notes**

Record:
- Design system in `packages/shared/src/theme/` and `packages/shared/src/components/`
- New shared components: FlxSidebar, FlxHeader, FlxFinancialCard, FlxQuickAction, FlxDrawer, FlxBottomNav
- Theme composable `useTheme` with dark/light toggle via `data-theme` attribute
- CSS tokens loaded via `@monetarie/shared/theme/tokens.css`
- New banking views: analytics, meds, fees, limits, ledger, transaction-search
- New merchant views: same 6 new pages
- Admin dashboard enhanced with charts

---

## Dependencies Between Tasks

```
Task 1 (tokens) → Task 2 (composable) → Task 6 (exports)
Task 3 (sidebar) → Task 6 (exports)
Task 4 (header) → Task 6 (exports)
Task 5 (cards etc) → Task 6 (exports)
Task 6 (exports) → Task 7, 8, 9 (integrations)
Task 7 (banking layout) → Task 10-13 (banking views)
Task 8 (merchant layout) → Task 14-15 (merchant views)
Task 9 (admin layout) → Task 16 (admin dashboard)
Task 17 (i18n) can run in parallel with 7-16
Task 18 (build) depends on all above
```
