AppChart

Universal chart component for data visualisation across all WLTH products. Renders pill-shaped bar charts, horizontal stacked bars, and more โ€” all styled with WLTH design tokens and responsive by default. Powered by CSS and Vue; no external charting library required.

Bar Height

180px

Breakpoint

5 bars
Tablet ยท 768px

Applications

$3.97m
$3.97mJan 26
$2.36mDec 25
$0Nov 25
$0Oct 25
$661.5kSept 25

Usage

<AppChart
  type="bar"
  :datasets="[
    {
      id: 'submitted',
      label: 'Submitted',
      total: '$3.97m',
      series: [{ id: 'submitted', label: 'Submitted Applications', color: 'royalblue' }],
      data: submittedData,
    },
    {
      id: 'inflow',
      label: 'In Flow',
      total: '$2.91m',
      series: [{ id: 'inflow', label: 'In Flow Applications', color: 'royalblue' }],
      data: inflowData,
    },
  ]"
  format="currency"
  :format-options="{ currency: '$' }"
  :show-dataset-toggle="true"
  :show-pagination="true"
  @select="(key, values) => console.log(key, values)"
/>

<!-- Highlight override: add highlight to any data point to force its bar colour -->
:data="[
  { key: 'q1', label: 'Q1', values: { val: 420 } },
  { key: 'q2', label: 'Q2', values: { val: 85 }, highlight: 'red' },
]"
<AppChart
  type="bar"
  :series="series"
  :data="data"
  format="currency"
/>

Components

AppChart

app/components/AppChart.vue

Entry-point wrapper. Routes to the correct renderer based on type. Provides KPI slot layout, dataset toggle, and loading/empty skeleton coordination.

AppChartBar

app/components/chart/AppChartBar.vue

Vertical pill bar renderer. Per-page normalisation, highest-value highlight, responsive window, pagination, hover tooltips, spring animation, keyboard nav.

AppChartStackedBar

app/components/chart/AppChartStackedBar.vue

Horizontal segmented bar renderer. Each series becomes a percentage-width segment. Includes hover tooltips and a legend.

Composables

useChartData

app/composables/useChartData.ts

Connects any async data source (Supabase, HubSpot, $fetch, etc.) to the chart component. Returns reactive data, isLoading, error, and refresh().

useChartFormat / formatChartValue

app/composables/useChartFormat.ts

Formats numeric values for chart labels. Modes: 'currency' ($661.5k, $3.97m), 'number' (12,563), 'percent' (25%), or a custom formatter function.

Connecting Real Data

Use useChartData to connect any API (Supabase, HubSpot, REST) without writing boilerplate. The composable handles loading state, errors, and data mapping automatically.

// Supabase example
import { useChartData } from '~/composables/useChartData'
import { useSupabaseClient } from '#imports'

const supabase = useSupabaseClient()

const { data, series, isLoading, refresh } = useChartData({
  source: async () => {
    const { data } = await supabase
      .from('broker_applications')
      .select('period_key, period_label, submitted_count')
      .order('period_key')
    return data ?? []
  },
  keyField: 'period_key',
  labelField: 'period_label',
  series: [
    { id: 'submitted', label: 'Submitted Applications', valueField: 'submitted_count', color: 'royalblue' }
  ]
})

// Then in template:
// <AppChart type="bar" :series="series" :data="data" :loading="isLoading" format="number" />
// $fetch / REST example
const { data, series, isLoading } = useChartData({
  source: () => $fetch('/api/payments/monthly'),
  keyField: 'periodKey',
  labelField: 'periodLabel',
  series: [
    { id: 'amex',         label: 'Amex',         valueField: 'amexPct'         },
    { id: 'mastercard',   label: 'Mastercard',   valueField: 'mastercardPct'   },
    { id: 'bankTransfer', label: 'Bank Transfer', valueField: 'bankTransferPct' },
  ]
})