Documentation Index Fetch the complete documentation index at: https://quill.co/docs/llms.txt
Use this file to discover all available pages before exploring further.
Used alongside useDashboardReport for fully customizable dashboards. Below is an example with shadcn showing fully custom styling.
import {
QuillProvider ,
useDashboard ,
useDashboardReport ,
StaticChart ,
} from "@quillsql/react" ;
import { Card , CardHeader } from "@/components/ui/card" ;
import { Skeleton } from "@/components/ui/skeleton" ;
function App () {
return (
< QuillProvider
tenants = { [{ tenantField: "customer_id" , tenantIds: [ 2 ] }] }
publicKey = { process . env . QUILL_PUBLIC_KEY }
>
< CustomDashboard />
</ QuillProvider >
);
}
function CustomDashboard () {
const { sections , isLoading } = useDashboard ( "quill demo dashboard" );
return (
<>
< ChartsSection reports = { sections [ "charts" ] } />
</>
);
}
function ChartsSection ({ reports } : { reports : any [] }) {
return (
< div className = "grid grid-cols-1 lg:grid-cols-6 gap-6" >
{ reports . map (( report : any ) => (
< ChartCard reportId = { report . id } name = { report . name } />
)) }
</ div >
);
}
function ChartCard ({ reportId , name }) {
const { report , loading } = useDashboardReport ( reportId );
if ( loading ) {
return (
< div className = "lg:col-span-1" >
< Card
className = "h-full shadow-none bg-transparent border-none"
title = { name }
>
< Skeleton />
</ Card >
</ div >
);
}
return (
< div className = "lg:col-span-1" >
< Card
className = "h-full shadow-none bg-transparent border-none"
title = { name }
>
< StaticChart reportId = { report . id } />
</ Card >
</ div >
);
}
Working with filters
useDashboard returns a filters array describing the dashboard’s available filters and an applyFilters function for updating them. Each filter has a type of "select", "multiselect", "date", or "tenant", plus a label, current value, and an options list.
The example below renders each filter type with a native control and applies updates via applyFilters:
import {
QuillProvider ,
useDashboard ,
StaticChart ,
} from "@quillsql/react" ;
function App () {
return (
< QuillProvider
tenants = { [{ tenantField: "customer_id" , tenantIds: [ 2 ] }] }
publicKey = { process . env . QUILL_PUBLIC_KEY }
>
< FilteredDashboard name = "quill demo dashboard" />
</ QuillProvider >
);
}
function FilteredDashboard ({ name } : { name : string }) {
const { sections , filters , applyFilters , isLoading } = useDashboard ( name );
return (
< div >
< div style = { { display: "flex" , gap: 12 , marginBottom: 16 } } >
{ filters . map (( filter ) => {
if ( filter . type === "select" ) {
return (
< select
key = { filter . label }
value = { filter . value ?? "" }
onChange = { ( e ) =>
applyFilters ([{ label: filter . label , value: e . target . value }])
}
>
< option value = "" > All </ option >
{ filter . options . map (( option ) => (
< option key = { option . value } value = { option . value } >
{ option . label }
</ option >
)) }
</ select >
);
}
if ( filter . type === "multiselect" ) {
return (
< select
key = { filter . label }
multiple
value = { filter . value }
onChange = { ( e ) => {
const value = Array . from ( e . target . selectedOptions ). map (
( option ) => option . value ,
);
applyFilters ([{ label: filter . label , value }]);
} }
>
{ filter . options . map (( option ) => (
< option key = { option . value } value = { option . value } >
{ option . label }
</ option >
)) }
</ select >
);
}
if ( filter . type === "date" ) {
return (
< select
key = { filter . label }
value = { filter . value . presetValue ?? "" }
onChange = { ( e ) => {
const preset = filter . options . find (
( option ) => option . value === e . target . value ,
);
if ( ! preset ) return ;
applyFilters ([
{
label: filter . label ,
value: {
startDate: preset . startDate ,
endDate: preset . endDate ,
},
},
]);
} }
>
{ filter . options . map (( option ) => (
< option key = { option . value } value = { option . value } >
{ option . label }
</ option >
)) }
</ select >
);
}
if ( filter . type === "tenant" ) {
// value is string | number | (string | number)[] | null
const selected = Array . isArray ( filter . value )
? filter . value . map ( String )
: filter . value === null
? ""
: String ( filter . value );
return (
< select
key = { filter . label }
multiple = { Array . isArray ( selected ) }
value = { selected }
onChange = { ( e ) => {
const value = Array . isArray ( selected )
? Array . from ( e . target . selectedOptions ). map (( o ) => o . value )
: e . target . value ;
applyFilters ([{ label: filter . label , value }]);
} }
>
{ ! Array . isArray ( selected ) && < option value = "" > All </ option > }
{ filter . options . map (( option ) => (
< option key = { option . value } value = { String ( option . value ) } >
{ option . label }
</ option >
)) }
</ select >
);
}
return null ;
}) }
</ div >
{ ! isLoading &&
sections &&
Object . values ( sections )
. flat ()
. map (( report ) => < StaticChart key = { report . id } reportId = { report . id } /> ) }
</ div >
);
}
Filter shapes
typevalueoptions"select"string{ label: string; value: string }[]"multiselect"string[]{ label: string; value: string }[]"date"{ presetValue?: string; startDate: Date | undefined; endDate: Date | undefined }{ label: string; value: string; startDate?: Date; endDate?: Date }[]"tenant"string | number | (string | number)[] | null{ label: string; value: string | number }[]
The "tenant" filter only appears if you have multiple Tenants defined in the BI platform and the viewing tenant has a mapping to the dashboard’s owner tenant. For example, a parent organization can use a multiselect tenant filter to view any subset of the child organizations it maps to.
applyFilters payload
applyFilters accepts an array of { label, value } objects. label must match a filter’s label from the filters array, and value must match the shape expected for that filter:
"select" — value: string
"multiselect" — value: string[]
"date" — value: { startDate?: Date; endDate?: Date; preset?: string }
"tenant" — value: string | string[] (coerce numeric tenant IDs to strings; pass an array when the tenant filter is multi-select)
Parameters
The name of the dashboard to load
Configuration options for the dashboard The number of rows per page for the tabular rows of a report. Defaults to
10.
Returns
Whether the dashboard data is currently loading
sections
Record<string, QuillReport[]> | null
The dashboard sections containing reports, organized by section name. See the
QuillReport reference for the full schema.
Available filters for the dashboard. These are typically rendered with html
select or similar UI components.
applyFilters
(filters: Array<{ label: string; value: string | string[] | { startDate?: Date; endDate?: Date }; } | Filter>) => void
Function to apply filters to the dashboard