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
180pxBreakpoint
5 barsApplications
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" />
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.
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.
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' },
]
})