# Architecture Source: https://quill.co/docs/architecture A new BI architecture, purpose built for end user facing, in-product dashboards and reporting. Traditionally in Business Intelligence products, there are 2 architectures, each with their own set of problems. | Architecture | Description | Problems | | --------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------- | | **Cloud** | A web app that you embed via an iframe | Limited customization, security concerns, performance issues | | **Self-hosted** | A docker container or kubernetes cluster hosted on your infrastructure | Complex setup, maintenance overhead, still requires iframe embedding | By building backend and frontend logic into your existing stack, we solved these problems. ## Hybrid Production DB connection runs in your cloud. Metadata requests are made to Quill Metadata API. BI Platform is hosted on our cloud. ## Self Hosted Production DB connection and BI Platform run entirely in your cloud. Metadata requests are made to Quill Metadata API. Typical for High Security Industries Like Finance, Healthcare, Government. ## Cloud DB connection and BI Platform run in our cloud, similar to Other Cloud Developer Products. # Create a dashboard Source: https://quill.co/docs/bi-platform/dashboard Build your first dashboard with Quill in less than a minute This guide assumes you have already connected your database with Quill. If you haven't done that yet, check out our onboarding guide [here](/bi-platform/quickstart). When you first get started with the Quill platform, you will see an environment without any dashboards. Click on the "create new dashboard" button to get started. ### 1. Create a dashboard Let's start by giving your new dashboard a name. The dashboard name should be unique, clear, and easy to remember. If you would like to automatically filter every chart in this dashboard by date, you can keep the date filter option selected. Additionally, if you would like to compare this date range against historical ranges (eg. compare "Last 90 days" against "Previous 90 days") you can select the `Date comparison` feature. Finally, if you would like to automatically filter every chart in this table by a string field on your schema (eg. filtering by user, merchant, etc.) you can add as many string filters as you would like. Remember, you can always create more than one dashboard. ### 2. Save your dashboard When you're done, go ahead and hit save. You should see your dashboard in the top-left of the screen. Since Quill automatically partitions by organization, you can select an organization in the top-right to filter any dashboard by (eg. "Acme") or you can select "All Organizations" to show all data. ### 3. Manage your dashboards If you ever want to create a new dashboard, click on the dashboard dropdown and select "New Dashboard". You can also edit the settings for any existing dashboard by selecting that dashboard and clicking "Manage" in the dropdown. That will open the Manage Dashboard modal which allows you to edit the title of the dashboard, add and remove filters, edit the order of charts, and delete the dashboard. ### Next Steps Now that you've created a dashboard, you're ready to create your first report! Check out our guide on how to create one below. Build your first report with Quill in less than a minute. # Overview Source: https://quill.co/docs/bi-platform/overview Relevant concepts in Quill BI Platform ## 1. Environments ### What is an Environment? An Environment is a single instance of Quill. Each environment has at least one tenant, a virtual schema, and dashboards that belong to it. You can promote changes in one environment to another environment on a report, dashboard, virtual table level. ## Common Example A Development and Production environment. ### Development * Points to development postgres database * Development Environment publicKey is used in your development and staging react apps * Development Environment privateKey is used in your development and staging server apps * When your server's schema changes, you can test those changes on the Development Environment * You can promote changes to the Production Environment when they are ready ### Production * Points to production postgres database * Production environment publicKey is used in your development and staging react apps * Production environment privateKey is used in your development and staging server apps ## 2. Tenant ### What is a Tenant? A tenant is a **logical partition** of data within a datasource. ### Tenant Roles * **Owner**: * Controls access to data at the **virtual table** level. * Dashboards must only use virtual tables owned by that tenant. * **Viewer**: * Can view dashboards if a **mapping** exists. * The mapping is a **SQL query** that defines which owners a viewer can access. *** ## 3. Virtual Table ### Creating a Virtual Table To create a virtual table: 1. Navigate to the **Virtual Schema** tab. 2. Click **Create Table**. ### What is a Virtual Table? A virtual table is: * A **SQL query** executed on top of your datasource. * Designed to **enforce tenancy** by requiring a tenant foreign key field (called an **owner**). * A mechanism for **cleaning and structuring** data for reporting purposes. > Note: A virtual table can have **multiple owners**. *** ## 4. Dashboard ### What is a Dashboard? A dashboard organizes and presents reports. It includes: * **Sections**: * Each section has a name and a list of reports. * **Global Filters**: * Apply across all reports in the dashboard. * Common filters include **date filters** and tenant-level fields. > A dashboard provides a unified interface to explore tenant-filtered, structured, and visualized data. *** ## 5. Report ### Creating a Report Once you have created at least one virtual table, you can create a report. ### What is a Report? A report is a SQL query based on the **virtual schema**. It supports the following views: * **Chart view** * **Tabular view** – A formatted output of the SQL query. * **Pivot (optional)** – Adds grouping and/or aggregation to the result, useful for visualizations. ### Report Features * **Access Control**: * Defines which **subset of tenants** can view the report on their dashboards. * **Dashboard Filter Field Mappings**: * Used when a common field appears across multiple tables. * Ensures proper linkage to dashboard-level filters. ### Report Placement * A report can be assigned to only **one dashboard**. * It can be placed in only **one section** within that dashboard. ### Promoting a report * You can promote a report from one dashboard to another, in the same environment *** ## Summary The virtual table reporting model consists of: 1. **Virtual Tables**: SQL-based, tenant-aware cleaned data structures. 2. **Reports**: Charted and tabular queries built from virtual tables. 3. **Tenants**: Access control units for data partitioning and security. 4. **Dashboards**: Grouped reports with global filters and sectioning. This structure ensures multi-tenant, clean, and secure reporting across your datasource. # Quickstart Source: https://quill.co/docs/bi-platform/quickstart Get up and running with Quill in less than a minute ### 1. Create a read-only user To create a read-only user in PostgreSQL, do the following: ```SQL PostgreSQL theme={null} -- Replace 'password' with a strong password. CREATE USER quill_read_only WITH PASSWORD 'password'; ``` ```SQL PostgreSQL theme={null} -- Allows the user to connect to the database. GRANT CONNECT ON DATABASE your_database TO quill_read_only; ``` ```SQL PostgreSQL theme={null} -- Allows the user to access the objects within the schema. GRANT USAGE ON SCHEMA your_schema TO quill_read_only; ``` ```SQL PostgreSQL theme={null} -- Allows the user to read data from the tables. GRANT SELECT ON ALL TABLES IN SCHEMA your_schema TO quill_read_only; ``` The connection string for the read-only user we just created will be something like: `postgresql://quill_read_only:password@db.example.com:5432/your_database`. To create a read-only user in Big Query, do the following: In the Google Cloud Console, navigate to the IAM & Admin > Service Accounts page. Click `Create Service Account` and follow the prompts to create a new service account. Grant the `bigquery.dataViewer` role to the service account for a dataset. You can do this by navigating to the dataset’s Permissions page and adding the service account with the `bigquery.dataViewer` role. Create a new JSON key for the service account by clicking the `Actions` dropdown next to the service account on the IAM & Admin > Service Accounts page, and then selecting `Create Key`. Select `JSON` as the key type and click `Create`. This will prompt you to save it one your local machine. In the next step you’ll upload this JSON file into the Quill BI Platform. To create a read-only user in Snowflake, do the following: ```SQL Snowflake SQL theme={null} -- Replace 'password' with a strong password. CREATE USER quill_read_only PASSWORD 'password'; ``` ```SQL Snowflake SQL theme={null} -- Creates a role specifically for read-only access. CREATE ROLE quill_read_only_role; ``` ```SQL Snowflake SQL theme={null} -- Allows the role to read data from the tables. GRANT SELECT ON ALL TABLES IN SCHEMA my_schema TO quill_read_only_role; ``` ```SQL Snowflake SQL theme={null} -- Gives user the permissions of the role GRANT quill_read_only_role TO quill_read_only; ``` To create a read-only user in MySQL, do the following: ```SQL MySQL theme={null} -- Replace 'password' with a strong password. CREATE USER 'quill_read_only'@'%' IDENTIFIED BY 'password'; ``` ```SQL MySQL theme={null} -- Allows the user to read data from the tables. GRANT SELECT ON your_database.* TO 'quill_read_only'@'%'; ``` The connection string for the read-only user we just created will be something like: `mysql://quill_read_only:password@db.example.com:3306/your_database`. ### 2. Connect your database Go to [https://app.quill.co/onboard](https://app.quill.co/onboard) and enter the read-only database connection string that we created in the last step. If you're using our fully-managed Quill instance, make sure to add the Quill server to your database IP whitelist, if you have one. Requests from Quill will always come from `34.133.137.225`, `209.71.81.30/32`, or `209.71.101.146` For more information about data and access control, you can read our self-hosting guide [here](/bi-platform/self-host). ### 3. Connect your schema The Quill platform provides powerful filtering and reporting tools to help you get the insights you want, faster. Since Quill provides organization-based filtering out-of-the-box, simply let us know which table to use and Quill will automatically partition your data by organization. ### 4. Add SQL Views Create a cleaned schema that makes it easier to write queries and reports. The views you create here will be used to create charts and tables later on. Don't worry, you can always create and edit these views later. ### Next Steps Once you have connected your database, you're ready to create your first dashboard! Check out our guides on how to create your first dashboard below. Build your first dashboard with Quill in less than a minute. # Create a report Source: https://quill.co/docs/bi-platform/report Build your first report with Quill in less than a minute This guide assumes you have already created a dashboard. If you haven't done that yet, check out our guide on how to create your first dashboard [here](/bi-platform/dashboard). There are two ways to create a report with Quill, with SQL or without SQL. Create a report with SQL Create a report from a user interface ## With SQL (SQLEditor) The SQL Editor is a powerful SQL-based code editor that you can use in your product to help your users create and edit their own reports. ### 1. Enter a query or ask AI Use the code editor to enter a SQL query. Alternatively, you can also use the text box to ask AI to write a SQL query for you using your available schemas. When you're finished with your query, click `Run Query` to execute that query against your database. ### 2. Edit your query Take a look at the response table to make sure the data was what you expected. If it isn't, you can either manually update the SQL query using the code editor or you can use the text box again to have the AI fix the query. If the query didn't work at all, you can also have the AI automatically fix the broken query by clicking `Fix with AI`: ### 3. Edit the chart Once you are satisfied with your query and the query results, click `Add to dashboard` in the bottom right to turn the query into a report. Here, you can edit the name of the chart, the dashboard this chart should belong to, the chart type, and much more. For a more detailed guide for each of each option, see [Editing a Report](#editing-a-report) below. ## No Code (ReportBuilder) The Report Builder is an easy-to-use, AI-enabled query creation tool that you can use in your product to allow nontechnical users to create and edit their own reports without knowing SQL. ### 1. Create a report or ask AI To create a report using the ReportBuilder, you can either ask AI to generate a report for you or use the interface on the left sidebar to select columns. ### 2. Edit the report Once you have a report that is populated with data, you can edit the report using AI or the left sidebar. To apply a filter, simply select the column you wish to filter against and then how you would like to filter (the options change based on the type of the column). To apply one- or two-dimensional groupings or aggregations you can add a pivot which will orient your data based on the columns and aggregation type you select. You can sort the data in the report by one or more columns using the sort feature and you can limit the number of rows returned in the report using the limit feature. ### 3. Edit the chart Once you are satisfied with your query and the query results, click `Add to dashboard` in the bottom right to turn the query into a report. Here, you can edit the name of the chart, the dashboard this chart should belong to, the chart type, and much more. For a more detailed guide for each of each option, see [Editing a Report](#editing-a-report) below. ## Editing a Report Once you have created a query, you can edit the resulting chart before adding it to your dashboard. You can edit the name of the chart, the dashboard this chart should belong to, the chart type, and much more. When used in an embedded flow where the dashboard is known ahead of time, developers can pre-select this dashboard and the option will be hidden in this form. ### Pivots Editing queries with SQL gives you fine-grain control over your data, but you can also add a pivot to the result of the query to apply extra groupings or breakdowns to get exactly the information you want. ### Chart Axis You can update the axis information for each chart by selecting which columns to use for each axis, what labels to give those columns (defaults to the name of the column), and the formatting type that should be applied to values for that axis. ### Table Info In Quill, every chart is backed by a SQL query. The results of that query are represented as a table which can be useful for reporting or sharing with other teams. Similar to the chart axis you can change the columns, labels, and formatting applied to this table. ### Date Field For dashboards with date filters, you can select which date field to use for this query. This column will be used when filtering all charts on that dashboard by a common date filter automatically. ### Organization Access By default, this chart will only be visible for your current organization (in this case, "Acme") but you can make this chart visible to all organizations by toggling the organization access toggle. This option can also be disabled. ## Next Steps With chart and a dashboard, you're ready to start using Quill. If you'd like, you can also add a new virtual table to give Quill access to even more data for making reports! Add a virtual table to Quill virtual schema. # Self-host the BI Platform Source: https://quill.co/docs/bi-platform/self-host Set up the BI Platform in your own react app If you want the BI Platform to point to a datasource via the server sdk (instead of Quill Cloud), you can add it to any react app in a few lines of code. ### 1. Install dependencies Install `@quillsql/admin` using your favorite package manager: ```bash npm theme={null} npm install @quillsql/admin ``` ```bash yarn theme={null} yarn add @quillsql/admin ``` ```bash pnpm theme={null} pnpm add @quillsql/admin ``` ```bash bun theme={null} bun add @quillsql/admin ``` ### 2. Add AdminProvider You connect Quill to React with the `AdminProvider` component. Similar to React's `Context.Provider`, `AdminProvider` wraps your React app and places Quill on the context, enabling you to access it from anywhere in your component tree. In App.js, let's wrap our React app with an `AdminProvider`. We suggest putting the `AdminProvider` somewhere high in your app, above any component that might use to access BI Platform components. If you're using `QuillProvider` in the same react app, make sure it is a child of `AdminProvider`. ```js App.js theme={null} import { AdminProvider } from "@quillsql/admin"; import { QuillProvider } from "@quillsql/react"; import MyApp from "./MyApp"; function App() { return ( ); } ``` Then, add two routes in your app, one for each page of the BI Platform. Use your existing navigation system, but here is an example using react router. ```js App.js theme={null} import { AdminProvider, VirtualTableManager, DashboardManager, } from "@quillsql/admin"; import { QuillProvider } from "@quillsql/react"; import { BrowserRouter, Routes, Route, Link, useNavigate, } from "react-router-dom"; function App() { return ( ); } function AppContent() { const navigate = useNavigate(); return (
navigate("/virtual-tables")} /> } /> navigate("/dashboards")} /> } />
); } ``` # Create a tenant Source: https://quill.co/docs/bi-platform/tenant Create objects that own dashboards and can view dashboards owned by other tenants Tenants do two things in the Quill platform. 1. separate data in a dashboard 2. allow certain organizations, users, etc to see specific reports in a dashboard. To learn more about tenancy read more [here](/bi-platform/overview#2-tenant). Tenants are the core concept for multi-tenancy in Quill. Each tenant represents a separate organization, user, etc. ### Tenant Configuration #### 1. Name Most commonly the business name. Example: "organization" #### 2. Division * **Row** (most common) - Each organization will have its own rows in a table via foreign key, `organization_id` * **Schema** - Each organization has its own schema * **Database** - Each organization has its own database instance #### 3. Roles * **Owner** - Owner is how dashboard data is divided * **Viewer** - Viewer can see some list of dashboards # Create a virtual table Source: https://quill.co/docs/bi-platform/virtual-table Add a virtual table to Quill virtual schema This guide assumes you have already connected your database with Quill. If you haven't done that yet, check out our onboarding guide [here](/bi-platform/quickstart). When you first get started with the Quill platform, you only see the initial views you created in the onboarding flow. ### 1. Create a virtual table To add a new SQL virtual table, you can click `Add virtual table +` to open a SQL Editor that shows the visible tables that Quill can see in your schema. SQL virtual tables can help you organize and group different data together to make it easier to build charts and tables on top of. ### 2. Edit the virtual table and save Go ahead and write some SQL for a new virtual table that you would like to create. Run the query by clicking `Run Query` and make sure the data looks correct. Give your new virtual table a descriptive, clear name and then hit `Create virtual table` to add the virtual table to Quill. You can now query data from that virtual table in charts and dashboards. ### 3. Manage your virtual tables If you ever want to edit or delete an existing virtual table, click on the virtual table in the SQL virtual tables list to open the virtual table manager. Be careful when editing and deleting virtual tables! Make sure the charts that reference that virtual table are up to date so they don't get out of sync. ### Next Steps Now that you have a virtual table, you're ready to start using Quill! You can come back at any time and add more dashboards, reports, and virtual tables. # Chart Source: https://quill.co/docs/react/chart The Chart view of a Quill Report ```tsx App.tsx theme={null} import { QuillProvider, Chart } from "@quillsql/react"; const MyChart = () => ( ); ``` A simple component that displays the given data in one of many chart types. Make sure `QuillProvider` is a parent of the `Chart` component. ### Automatically fetch data by id If you know the id of the chart you would like to display, you can pass in the reportId to the Chart component and it will load and display the data for that chart. ```jsx theme={null} import { QuillProvider, Chart } from "@quillsql/react"; function App() { return ( ); } ``` ## Props The chart id. The most usage is through a detail page built to navigate from the dashboard - using the onClick callback to get the reportId, and navigating to a route (say, reports/:id) where the url param is passed in as the reportId. For a standalone table, you can find the reportId in the Quill BI Platform and pass it in directly. When config is passed, the chart will not refetch the given report and will instead simply render the report it was given. A `config` must be passed if `reportId` is not present. A list of color strings used to color the chart. For example, a pie chart would use the colors for each section and a bar chart would use the colors for each bar. Whether to show animations on render complete. Whether to hide the x axis. Whether to hide the y axis. Whether to hide the cartesian grid lines. Whether the date range filter should be hidden. Whether to hide the horizontal cartesian grid lines. Whether to hide the vertical cartesian grid lines. Whether to hide the all but the first of the X-Axis ticks. Whether the cartesian grid lines show as dashed or solid. The color of cartesian grid lines. Whether the comparison range shows as dashed for date comparison line charts (as opposed to the default solid line). An optional function that takes a report and theme and returns a map of keys used in that report to the colors they should use. The color values support RGB hexcodes and CSS color literals. ```js theme={null} function mapColorsToFields(report, theme): ColorMapType { return { amount: { primary: 'red', comparison: 'gray', primaryGradientStart: 'red', primaryGradientStop: 'lightred', comparisonGradientStart: '#EFEFEF', comparisonGradientStop: '#EFEFEF00', }, total: { primary: 'red' }, }; } ``` ### ColorMapType ```ts theme={null} export type ColorMapType = { [field: string]: { primary: string; comparison?: string; primaryGradientStart?: string; primaryGradientStop?: string; comparisonGradientStart?: string; comparisonGradientStop?: string; }; } ``` Styles the top-level container of the Chart. This can be useful for TailwindCSS-style classname strings. The CSS styles that wrap the chart. # Dashboard Source: https://quill.co/docs/react/dashboard A list of Quill Reports grouped into Metrics, Charts, and Tables. ```tsx App.tsx theme={null} import { QuillProvider, Dashboard } from "@quillsql/react"; function App() { return ( ); } ``` Dynamically displays a filterable grid of charts, metrics, and tables with live data from your database. Once implemented, Quill lets you perform zero-downtime, zero-code updates to your dashboard on an org-level as well as company-wide. Make sure `QuillProvider` is a parent of the `Dashboard` component. Don't have a dashboard name yet? Learn how to [create a dashboard](/bi-platform/dashboard) in the Quill BI Platform to get started. ## Examples [![Edit \[Ant Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/ant-design-quill-react-components-9qcs8k?embed=1) ```tsx theme={null} import { Dashboard } from "@quillsql/react"; import { AntDateRangePickerComponent } from "./ui/ant/DateRangePickerComponent"; import { AntTableComponent } from "./ui/ant/TableComponent"; import { AntSelectComponent } from "./ui/ant/SelectComponent"; import { AntChartComponent } from "./ui/ant/ChartComponent"; import { AntMetricComponent } from "./ui/ant/MetricComponent"; export function AntDashboard() { return ( ); } ``` [![Edit \[Material Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/material-design-quill-react-components-d862dk?embed=1) ```tsx theme={null} import { Dashboard } from "@quillsql/react"; import { MaterialDateRangePickerComponent } from "./ui/material/DateRangePickerComponent"; import { MaterialTableComponent } from "./ui/material/TableComponent"; import { MaterialSelectComponent } from "./ui/material/SelectComponent"; import { MaterialChartComponent } from "./ui/material/ChartComponent"; import { MaterialMetricComponent } from "./ui/material/MetricComponent"; import { MaterialFilterContainerComponent } from "./ui/material/Layout"; export function MaterialDashboard() { return ( ); } ``` ## Props The name of the dashboard you created in the Quill BI Platform. A component that wraps a report for 'metric' chart types. The current report. See the API Reference for a `QuillReport` [here](/components/dashboard#quill-report). A callback that should be fired when the user clicks on this report. Whether this report is loading. An error message that resulted from the loading of this report, if any. A component that wraps a report for non-metric, non-table chart types. The current report. See the API Reference for a `QuillReport` [here](/components/dashboard#quill-report). A callback that should be fired when the user clicks on this report. Whether this report is loading. An error message that resulted from the loading of this report, if any. The child of a chart component is a chart graph by default. You can override this behavior by not passing children in your implemented version and instead rendering your own Chart. A component that wraps a report for 'table' chart types. The current report. See the API Reference for a `QuillReport` [here](/components/dashboard#quill-report). A callback that should be fired when the user clicks on this report. Whether this report is loading. An error message that resulted from the loading of this report, if any. A select component. ```jsx Example theme={null} export function SelectComponent({ value, label, width, onChange, options }) { return ( ); } ``` The value of the select element. The width of the select element in pixels. The label above the select element. An event callback that is fired when the select value changes. An array of value, label pairs which represent the select options. A date range picker component. The date preset. The label above the date range picker. The current date range. A set of options to select presets from. A callback that is fired when the selected date range changes. An event callback that is fired when the preset value changes. A component that wraps each dashboard section. Each dashboard is grouped into three sections: metrics, charts, and tables (in that order). Some sections may be empty, in which case they are omitted. The name of the section. The children of the dashboard section. A component that wraps all dashboard sections. The children of the dashboard section. A component that wraps the row of dashboard filters. The children of the filter container. A fallback component displayed when an active dashboard has no reports. Callback function that fires when a report is clicked. A common use case is navigating to a new page based on the report's id. A dashboard is composed of a list of reports that could be metrics, charts, or tables. See the API Reference for a `QuillReport` [here](/components/dashboard#quill-report). A callback that is fired when the loading status of the dashboard changes. Whether to hide dashboard filters. Whether the date range filters on the dashboard are hidden. Whether to hide the xAxis for all charts on this dashboard. Whether to hide the yAxis for all charts on this dashboard. Whether to hide the cartesian grid for all charts on this dashboard. The line style for comparison ranges on all line charts on this dashboard. Applies the following classes to the ReportBuilder. This container is the parent of all the filtering buttons as well as any dashboard sections. This can be useful for TailwindCSS-style classname strings. The CSS styles that wrap the dashboard container. This container is the parent of all the filtering buttons as well as any dashboard sections. The styles for the chart container. ## QuillReport The report's unique id. The name of the report. The name of the dashboard this report belongs to. The rows of data returned from this report's query. The columns data returned from this report's query. The type of this chart. The table and field this chart uses for date filtering. The pivot used in this query, if any. The pivot title. The aggregation type for the values in this pivot. The row field. The type of the row field. The column field, if any. The type of the column field, if any. The value field, if any. The type of the value field, if any. The formatted primary range aggregation value, if any. The formatted comparison range aggregation value, if any. The percent change in the aggragations, if any. The label of the xAxis. The field to use for the report's xAxis. The format for the report's xAxis. A list of metadata bout the yAxes of this report. The relative ordering of this report in relation to its siblings. Ordering starts at 1 and counts up. Reports in the same section are first grouped by `chartType` and then each group is sorted by `order`. Currently, the first group is `metric` and the last group is `table` with the rest of the reports in between. The rows of data returned from this report's query over the comparison date range as opposed to the primary date range. A map of filters that have been applied to this query. # format Source: https://quill.co/docs/react/format A simple way to format data from Quill ```tsx App.tsx theme={null} import { format } from "@quillsql/react"; function formatExample() { format(123.45, "dollar_cents"); // "$123.45" format(123, "dollar_cents"); // "$123.00" } ``` The value of the thing being formatted. The Quill format type of how you want to format the value. # What is Headless UI? Source: https://quill.co/docs/react/headless "Headless" in React refers to a design pattern where UI components are separated into two parts: 1. **Logic/State Management** - The "headless" part that handles all the business logic, state, and behavior 2. **UI/Presentation** - The visual components that you style and customize ## How Headless Components Work Headless components provide all the functionality without any default styling or visual elements. They give you: * Data processing * State management * Business logic But leave the UI completely up to you. > The hardest parts of building complex UIs usually revolve around state, events, side-effects, data computation/management. By removing these concerns from the markup, styles and implementation details, our logic and components can be more modular and reusable. — [**TanStack**](https://tanstack.com/table/v8/docs/introduction) ## Popular Libraries that leverage React Hooks * [TanStack Table (useReactTable)](https://tanstack.com/table/latest) * [TanStack Query (useQuery)](https://tanstack.com/query/latest) * [Clerk React SDK (useUser)](https://clerk.com/docs/hooks/use-user) * [React Hook Form (useForm)](https://react-hook-form.com/docs/useform) * [React Window (useVirtualizer)](https://tanstack.com/virtual/latest) * [React DnD (useDrag, useDrop)](https://react-dnd.github.io/react-dnd/docs/overview) # Headless dashboard Source: https://quill.co/docs/react/headless-dashboard Add a dashboard to your app, styled with your existing UI components. ```tsx App.tsx theme={null} import { QuillProvider, useDashboard, useDashboardReport, format, } from "@quillsql/react"; import { Card, CardHeader } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; function App() { return ( ); } function CustomDashboard() { const { sections, isLoading } = useDashboard("quill demo dashboard"); return ( <> ); } function MetricsSection({ reports }: { reports: any[] }) { return (
{reports.map((report: any) => ( ))}
); } interface MetricCardProps { reportId: string; name: string; } export function MetricCard({ reportId, name }: MetricCardProps) { const { report, loading } = useDashboardReport(reportId); if (loading) { return (

{name}

); } if (!report) return null; const mainValue = format({ value: report.pivotRows?.[0]?.[report.xAxisField], format: report.xAxisFormat, }); return (

{mainValue}

{name}

); } ``` # Headless CSV and PDF export Source: https://quill.co/docs/react/headless-data-export Add a report detail page to your app with CSV and PDF export, styled with your existing UI components. # Quickstart Source: https://quill.co/docs/react/quickstart Add Quill to your app in less than a minute ### 1. Install dependencies Install `@quillsql/react` using your favorite package manager: ```bash npm theme={null} npm install @quillsql/react ``` ```bash yarn theme={null} yarn add @quillsql/react ``` ```bash pnpm theme={null} pnpm add @quillsql/react ``` ```bash bun theme={null} bun add @quillsql/react ``` ### 2. Add QuillProvider You connect Quill to React with the `QuillProvider` component. Similar to React's `Context.Provider`, `QuillProvider` wraps your React app and places Quill Client on the context, enabling you to access it from anywhere in your component tree. In App.js, let's wrap our React app with an `QuillProvider`. We suggest putting the `QuillProvider` somewhere high in your app, above any component that might need to access Quill data. ```js App.js theme={null} import { QuillProvider } from "@quillsql/react"; import MyApp from "./MyApp"; function App() { return ( ); } ``` ### 3. Add your first component After your QuillProvider is hooked up, you can add Quill Components to your app. Let's start with the dashboard we created in the [BI Platform Tutorial](/bi-platform/quickstart). You can find the dashboard **name** in the BI Platform at [https://app.quill.co](https://app.quill.co). Underlying queries and charts can be updated via the Quill BI Platform, and the dashboard will render the newest version. ```js App.js theme={null} import { QuillProvider, Dashboard } from "@quillsql/react"; function MyDashboardPage() { return ; } ``` # QuillProvider Source: https://quill.co/docs/react/quill-provider A context provider that wraps all quill components ```tsx App.tsx theme={null} import { QuillProvider } from "@quillsql/react"; function App() { return ( {children} ); } ``` The quill provider allows all the quill components in your app to share information which lets your dashboards render fast and update dynamically. Similar to React's `Context.Provider`, `QuillProvider` wraps your React app and places Quill Client on the context, enabling you to access it from anywhere in your component tree. We suggest putting the QuillProvider somewhere high in your app, above any component that might need to access your quill data. ### With OrgId If you're using the Quill Cloud, you can pass in your public key and the current organizationId (if any) and the Quill Provider will automatically connect to the hosted Quill Cloud. ```jsx With OrgId theme={null} {children} ``` ### With QueryEndpoint If you're self-hosting Quill, you can point the Quill Provider to the location of the server running the Quill SDK. You may also pass a map of query headers that will be forwarded to your server with every request Quill sends. This can be useful if the `/quill` endpoint is behind a preexisting auth middleware. ```jsx With QueryEndpoint theme={null} {children} ``` ## Props The public Quill API key. This can be found in the Quill BI Platform by clicking "Manage" on an environment in the environment dropdown -> "Copy public key". The environment this app is running in (eg. "production"). The tenant ids of the user. Required if not passing a queryEndpoint. If this value is not passed in on the frontend, it should be passed in on the backend if you are using a self-hosted server. The url of your self-hosted server running the quill server SDK, if any. Additional query headers passed along with all requests to the custom query endpoint, if any. Whether to include credentials with requests to the query endpoint. A custom theme used throughout your dashboard. The children of the provider. This is usually the rest of your app. # ReportBuilder Source: https://quill.co/docs/react/report-builder A UI component for creating and editing Quill Reports ```tsx App.tsx theme={null} import { QuillProvider, ReportBuilder } from "@quillsql/react"; function App() { return ( ); } ``` Allows non-technical users to build SQL queries using either UI or AI and then edit them on the fly. Once users have constructed a query they like, they can click a button and add that report to their dashboard or export it as a CSV. Make sure `QuillProvider` is a parent of the `ReportBuilder` component. ## Examples [![Edit \[Ant Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/ant-design-quill-react-components-9qcs8k?embed=1) ```tsx Ant Design theme={null} import { ReportBuilder } from "@quillsql/react"; import { AntButton, AntDeleteButton, AntSecondaryButton, } from "./ui/ant/Button"; import { AntCard } from "./ui/ant/Card"; import { AntCheckbox } from "./ui/ant/Checkbox"; import { AntSelectColumn, AntDraggableColumn, AntColumnSearchEmptyState, } from "./ui/ant/Column"; import { AntTextInput } from "./ui/ant/Input"; import { AntChartBuilderFormContainer, AntChartBuilderInputColumnContainer, AntChartBuilderInputRowContainer, AntContainer, AntErrorMessageComponent, AntLoadingComponent, AntPivotColumnContainer, AntPivotRowContainer, AntSidebar, } from "./ui/ant/Layout"; import { AntModal } from "./ui/ant/Modal"; import { AntPopover, AntFilterPopover, AntSortPopover, AntLimitPopover, } from "./ui/ant/Popover"; import { AntSelect } from "./ui/ant/Select"; import { AntTable } from "./ui/ant/Table"; import { AntTabs } from "./ui/ant/Tabs"; import { AntSidebarHeading, AntLabel, AntHeader, AntText, AntSubHeader, } from "./ui/ant/Typography"; export function AntReportBuilder() { return ( ); } ``` [![Edit \[Material Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/material-design-quill-react-components-d862dk?embed=1) ```tsx Material Design theme={null} import { ReportBuilder } from "@quillsql/react"; import { MaterialButton, MaterialDeleteButton, MaterialSecondaryButton, } from "./ui/material/Button"; import { MaterialCard } from "./ui/material/Card"; import { MaterialCheckbox } from "./ui/material/Checkbox"; import { MaterialSelectColumn, MaterialDraggableColumn, MaterialColumnSearchEmptyState, } from "./ui/material/Column"; import { MaterialTextInput } from "./ui/material/Input"; import { MaterialChartBuilderFormContainer, MaterialChartBuilderInputColumnContainer, MaterialChartBuilderInputRowContainer, MaterialContainer, MaterialErrorMessageComponent, MaterialPivotColumnContainer, MaterialPivotRowContainer, MaterialSidebar, } from "./ui/material/Layout"; import { MaterialModal } from "./ui/material/Modal"; import { MaterialPopover, MaterialFilterPopover, MaterialSortPopover, MaterialLimitPopover, } from "./ui/material/Popover"; import { MaterialSelect } from "./ui/material/Select"; import { MaterialTable } from "./ui/material/Table"; import { MaterialTabs } from "./ui/material/Tabs"; import { MaterialSidebarHeading, MaterialLabel, MaterialHeader, MaterialText, MaterialSubHeader, } from "./ui/material/Typography"; export function MaterialReportBuilder() { return ( ); } ``` ### Prefetch data from a particular table You can pass an `initialTableName` to have the report builder automatically load data from a particular table in your schema. ```tsx App.tsx theme={null} import { QuillProvider, ReportBuilder } from "@quillsql/react"; function App() { return ( ); } ``` ### Edit an existing report You can pass a `reportId` to have the report builder load the metadata for a pre-existing report. This might be useful if you want to allow your end users to edit the reports they created. ```tsx App.tsx theme={null} import { QuillProvider, ReportBuilder } from "@quillsql/react"; function App() { return ( ); } ``` ### Query all views in your Quill schema By default, the Quill Report Builder will query all tables in your schema and new reports will be created upon completion. ```tsx App.tsx theme={null} import { QuillProvider, ReportBuilder } from "@quillsql/react"; function App() { return ( ); } ``` ## Props The default table name to show when first loading the ReportBuilder. See the [prefetch data example](/components/report-builder#prefetch-data-from-a-particular-table) to see an example of how this is used. Sets the dashboard of a report created in the report builder. Checkout our [UI examples](/components/examples) to see how this is used. Sets the dashboard section of a report created in the report builder. Checkout our [UI examples](/components/examples) to see how this is used. A primary button component. ```jsx Example theme={null} export function ButtonComponent({ onClick, label, disabled, icon }) { return ( ); } ``` A callback fired when the button is clicked. The text content of the button. Whether the button is disabled. An icon to put in front of the label. A secondary button component. ```jsx Example theme={null} export function SecondaryButtonComponent({ onClick, label, disabled, icon }) { return ( ); } ``` A callback fired when the button is clicked. The text content of the button. Whether the button is disabled. An icon to put in front of the label. A small delete button used to click out of things. Usually an "X" icon. ```jsx Example theme={null} import { deleteSVG } from "./ui/icons"; export function DeleteButtonComponent({ onClick }) { return ; } ``` A callback fired when the button is clicked. A input element for getting text from the user. ```jsx Example theme={null} export function TextInputComponent({ id, width, value, label, placeholder, onChange }) { return ( ); } ``` The id of the input element. The width of the input element in pixels. The width of the input element in pixels. The label of the text input component. The placeholder for the input element. An event callback that is fired when the input value changes. A modal component. ```jsx Example theme={null} import { Modal } from "./ui/modal"; export function ModalComponent({ isOpen, setIsOpen, title, children, width, height }) { return ( {children} ); } ``` Whether the modal is open. A callback to set whether the modal is open. The title of the modal, if any. The body of the modal. The width of the modal, in pixels. The height of the modal, in pixels. A modal component. ```jsx Example theme={null} import { Modal } from "./ui/modal"; export function ChartBuilderModalComponent({ isOpen, setIsOpen, title, children, width, height }) { return ( {children} ); } ``` Whether the modal is open. A callback to set whether the modal is open. The title of the modal, if any. The body of the modal. The width of the modal, in pixels. The height of the modal, in pixels. A select component. ```jsx Example theme={null} export function SelectComponent({ value, label, width, onChange, options }) { return ( ); } ``` The value of the select element. The label above the select element. The width of the select element, in pixels. An event callback that is fired when the select value changes. An array of value, label pairs which represent the select options. A table component. ```jsx Example theme={null} import { Table } from "./ui/table"; import { LoadingSkeleton } from "./ui/loading"; export function TableComponent({ rows, columns, isLoading }) { if (isLoading) return return ; } ``` The rows of the table are an array of objects. The columns of the table are an array of label, field pairs. Whether the table is loading. A popover component. ```jsx Example theme={null} import { Popover } from "./ui/popover"; export function PopoverComponent({ isOpen, setIsOpen, popoverTitle, popoverChildren }) { return ( {popoverChildren} ); } ``` Whether the popover is open. A callback to set whether the popover is open. The label of the popover trigger, if any. The title of the popover, if any. The body of the popover. A popover component for filters created in the report builder. ```jsx Example theme={null} import { Popover } from "./ui/popover"; export function FilterPopoverComponent({ isOpen, setIsOpen, popoverTitle, popoverChildren }) { return ( {popoverChildren} ); } ``` Whether the popover is open. A callback to set whether the popover is open. The title of the popover, if any. The body of the popover. The text contents of the filter item. An event callback fired when the user clicks the delete button on the filter. A popover component for sort items created in the report builder. ```jsx Example theme={null} import { Popover } from "./ui/popover"; export function SortPopoverComponent({ isOpen, setIsOpen, popoverTitle, popoverChildren }) { return ( {popoverChildren} ); } ``` Whether the popover is open. A callback to set whether the popover is open. The title of the popover, if any. The body of the popover. The text contents of the sort item. An event callback fired when the user clicks the delete button on the sort item. A popover component for limits created in the report builder. ```jsx Example theme={null} import { Popover } from "./ui/popover"; export function LimitPopoverComponent({ isOpen, setIsOpen, popoverTitle, popoverChildren }) { return ( {popoverChildren} ); } ``` Whether the popover is open. A callback to set whether the popover is open. The title of the popover, if any. The body of the popover. The text contents of the limit. An event callback fired when the user clicks the delete button on the limit. A small navigation menu used to switch between two or more states. ```jsx Example theme={null} import { Tabs, Tab } from "./ui/tabs"; export function TabsComponent({ value, onChange, options }) { return ( {options.map(({ value, label }) => {label})} ); } ``` The value of the currently selected tab. An event callback fired when the selected tab changes. An array of value, label pairs which represent the tabs to display. A checkbox component. ```jsx Example theme={null} export function CheckboxComponent({ isChecked, label, onChange }) { return ( ); } ``` Whether the checkbox is checked. A label for the checkbox component. An event callback fired when the checkbox's value changes. A container for the left sidebar. ```jsx Example theme={null} export function SidebarComponent({ children }) { const style = { display: "flex", flexDirection: "column", gap: 24, } return } ``` The children of the sidebar container. A container for the main content (everything right of the sidebar). ```jsx Example theme={null} export function ContainerComponent({ children }) { const style = { display: "flex", flexDirection: "column", gap: 24, } return
{children}
} ``` The children of the main container.
A component to show while the query results are loading. A component to show selected columns. ```jsx Example theme={null} export function SelectColumnComponent({ label, isSelected, setSelected, DragHandle }) { return (
{label}
); } ``` The label of the selected column. Whether this column has been selected. A callback that is fired when the user checks this columns' checkbox. The handle the user drags this column by.
A draggable component used to reorder columns. ```jsx Example theme={null} import { DeleteSVG } from "./ui/icons"; export function DraggableColumnComponent({ label, onDelete, DragHandle }) { return (
{label}
); } ``` The label of the draggable column. A callback that is fired when this column is removed from the query. The handle the user drags this column by.
A heading element for the sidebar. ```jsx Example theme={null} export function SidebarHeadingComponent({ label }) { return

{label}

; } ``` The text content of the sidebar heading.
A card component used as a dismissable container of pivot information. ```jsx Example theme={null} import { Card } from "./ui/card"; export function CardComponent({ onClick, onDelete, children }) { const style = { position: 'absolute', top: 0, right: 0 }; return ( {onDelete && } {children} ); } ``` The children of the container. A callback that is fired when the card is clicked. A callback that is fired the card is deleted. A label component. ```jsx Example theme={null} export function LabelComponent({ label }) { return

{label}

; } ``` The text content of the element.
A header component. ```jsx Example theme={null} export function HeaderComponent({ label }) { return

{label}

; } ``` The text content of the element.
A simple text component. ```jsx Example theme={null} export function TextComponent({ label }) { return

{label}

; } ``` The text content of the element.
A sub-header component describes a group of inputs. ```jsx Example theme={null} export function SubHeaderComponent({ label }) { return

{label}

; } ``` The label of the sub-header component.
A container for each row of inputs for the ChartBuilder form. ```jsx Example theme={null} export function ChartBuilderInputRowContainer({ children }) { const style = { display: "flex", flexDirection: "row", gap: 12, } return
{children}
} ``` The children of the container.
A container for vertically-stacked rows of inputs for the ChartBuilder form. ```jsx Example theme={null} export function ChartBuilderInputColumnContainer({ children }) { const style = { display: "flex", flexDirection: "column", gap: 12, } return
{children}
} ``` The children of the container.
A container for each row of inputs for the pivot form. ```jsx Example theme={null} export function PivotRowContainer({ children }) { const style = { display: "flex", flexDirection: "row", gap: 12, } return
{children}
} ``` The children of the container.
A container for vertically-stacked rows of inputs for the pivot form. ```jsx Example theme={null} export function PivotColumnContainer({ children }) { const style = { display: "flex", flexDirection: "column", gap: 12, } return
{children}
} ``` The children of the container.
A container for vertically-stacked sections of the chart builder form. ```jsx Example theme={null} export function ChartBuilderFormContainer({ children }) { const style = { display: "flex", flexDirection: "column", gap: 18, } return
{children}
} ``` The children of the container.
A component that displays error messages. ```jsx Example theme={null} export function ErrorMessageComponent({ errorMessage }) { return
{errorMessage}
} ``` The error message.
A component to show when no columns match the user's query. Whether the ReportBuilder is in admin mode (default: `false`). Whether the ReportBuilder's AI features are enabled (default: `true`). Whether the PivotModal's AI features are enabled. Whether to show the table format options on the ChartBuilder form. Applies the following classes to the ReportBuilder. This can be useful for TailwindCSS-style classname strings. Custom styling properties for the ReportBuilder's top-level container. A report id that the Report Builder will query from and modify. See the [report id example](#edit-an-existing-report) to see an example of how this is used. A callback function that will trigger when a new report is created. A callback function that will trigger when a existing report is edited. Whether to hide the copy SQL button. Whether the chart builder is in horizontal view mode. Horizontal view mode is where the chart and table are displayed on the left and the editing form is displayed on the right, rather than being stacked vertically. # SQLEditor Source: https://quill.co/docs/react/sql-editor A UI component for creating and editing Quill Reports with SQL ```tsx App.tsx theme={null} import { QuillProvider, SQLEditor } from "@quillsql/react"; function App() { return ( ); } ``` Allows your users to build and write custom SQL queries and then add those queries into their dashboard as a metric, chart, or table. Make sure `QuillProvider` is a parent of the `SQLEditor` component. ## Examples [![Edit \[Ant Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/ant-design-quill-react-components-9qcs8k?embed=1) ```tsx theme={null} import { SQLEditor } from "@quillsql/react"; import { AntTable } from "./ui/ant/Table"; import { AntSelectComponent } from "./ui/ant/SelectComponent"; import { AntButton, AntSecondaryButton } from "./ui/ant/Button"; import { AntTextInput } from "./ui/ant/Input"; export function AntSQLEditor() { return ( ); } ``` [![Edit \[Material Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/material-design-quill-react-components-d862dk?embed=1) ```tsx theme={null} import { SQLEditor } from "@quillsql/react"; import { MaterialTable } from "./ui/material/Table"; import { MaterialSelect } from "./ui/material/Select"; import { MaterialButton, MaterialSecondaryButton } from "./ui/material/Button"; import { MaterialTextInput } from "./ui/material/Input"; export function MaterialSQLEditor() { return ( ); } ``` ## Props A primary button component. ```jsx Example theme={null} export function ButtonComponent({ onClick, label, disabled, icon }) { return ( ); } ``` A callback fired when the button is clicked. The text content of the button. Whether the button is disabled. An icon to put in front of the label. A secondary button component. ```jsx Example theme={null} export function SecondaryButtonComponent({ onClick, label, disabled, icon }) { return ( ); } ``` A callback fired when the button is clicked. The text content of the button. Whether the button is disabled. An icon to put in front of the label. A small delete button used to click out of things. Usually an "X" icon. ```jsx Example theme={null} import { deleteSVG } from "./ui/icons"; export function DeleteButtonComponent({ onClick }) { return ; } ``` A callback fired when the button is clicked. A input element for getting text from the user. ```jsx Example theme={null} export function TextInputComponent({ id, width, value, label, placeholder, onChange }) { return ( ); } ``` The id of the input element. The width of the input element in pixels. The width of the input element in pixels. A label for the text input. The placeholder for the input element. An event callback that is fired when the input value changes. A select element for letting the user select from a set of options. ```jsx Example theme={null} export function SelectComponent({ value, label, width, onChange, options }) { return ( ); } ``` The value of the select. A label for the select. The select options. An event callback that is fired when the selected element changes. The width of the select element in pixels. A table component. ```jsx Example theme={null} import { Table } from "./ui/table"; import { LoadingSkeleton } from "./ui/loading"; export function TableComponent({ rows, columns, isLoading }) { if (isLoading) return return
; } ``` The rows of the table are an array of objects. The columns of the table are an array of label, field pairs. Whether the table is loading. A component to show while the query results are loading. A card component used as a dismissable container of pivot information. ```jsx Example theme={null} import { Card } from "./ui/card"; export function CardComponent({ onClick, onDelete, children }) { const style = { position: 'absolute', top: 0, right: 0 }; return ( {onDelete && } {children} ); } ``` The children of the container. A callback that is fired when the card is clicked. A callback that is fired the card is deleted. A modal component to use to open the add to dashboard dialog. ```jsx Example theme={null} import { Modal } from "./ui/modal"; export function ModalComponent({ isOpen, setIsOpen, title, children, width, height }) { return ( {children} ); } ``` Whether the modal is open. A callback to set whether the modal is open. The title of the modal, if any. The body of the modal. The width of the modal, in pixels. The height of the modal, in pixels. A popover component. ```jsx Example theme={null} import { Popover } from "./ui/popover"; export function PopoverComponent({ isOpen, setIsOpen, popoverTitle, popoverChildren }) { return ( {popoverChildren} ); } ``` Whether the modal is open. A callback to set whether the modal is open. The label for the trigger of this popover. The title of the popover. The children of this popover. A label component. ```jsx Example theme={null} export function LabelComponent({ label }) { return

{label}

; } ``` The label of the label component.
A header component. ```jsx Example theme={null} export function HeaderComponent({ label }) { return

{label}

; } ``` The label of the header component.
A sub-header component describes a group of inputs. ```jsx Example theme={null} export function SubHeaderComponent({ label }) { return

{label}

; } ``` The label of the sub-header component.
A simple text component. ```jsx Example theme={null} export function TextComponent({ label }) { return

{label}

; } ``` The label of the text component.
A container for each row of inputs for the ChartBuilder form. The children of the container. A container for vertically-stacked rows of inputs for the ChartBuilder form. The children of the container. A container for each row of inputs for the pivot form. The children of the container. A container for vertically-stacked rows of inputs for the pivot form. The children of the container. A container for vertically-stacked sections of the chart builder form. The children of the container. A component that displays error messages. ```jsx Example theme={null} export function ErrorMessageComponent({ errorMessage }) { return
{errorMessage}
} ``` The error message.
A callback that is fired when the query changes. A callback that is fired when the data changes. A callback that is fired when the data fields change. A callback that is fired when the data columns change. A callback that is fired when a new report has been added to a dashboard. Whether the ReportBuilder is in admin mode (default: `true`). A callback that is fired when the data columns change. Whether the "new query" button is enabled. Whether to show table format options. Whether to show date field options. Whether to show access control options. An existing report to edit. The default query to use as a placeholder. The default dashboard to add reports to. The title of the ChartBuilder dialog. The label of the button to add the current query to a dashboard. The label of the button to open the ChartBuilder dialog. The name of the current organization. Styles the top-level container of the SQLEditor. This can be useful for TailwindCSS-style classname strings. Custom styling properties for the ReportBuilder's top-level container. # StaticChart Source: https://quill.co/docs/react/static-chart Stateless Chart view of a Quill Report Stateless chart component intended for use with [useDashboard](/react/use-dashboard) and [useDashboardReport](/react/use-dashboard-report). ```tsx App.tsx theme={null} import { QuillProvider, useDashboard, useDashboardReport, StaticChart, } from "@quillsql/react"; import { Card, CardHeader } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; function App() { return ( ); } function CustomDashboard() { const { sections, isLoading } = useDashboard("quill demo dashboard"); return ( <> ); } function ChartsSection({ reports }: { reports: any[] }) { return (
{reports.map((report: any) => ( ))}
); } function ChartCard({ reportId, name }) { const { report, loading } = useDashboardReport(reportId); if (loading) { return (
); } return (
); } ``` The unique identifier of the report to display Callback function triggered when a chart element is clicked Custom CSS styles to apply to the chart container Whether to display the chart legend # Table Source: https://quill.co/docs/react/table The Tabular view of a Quill Report ```tsx App.tsx theme={null} import { QuillProvider, Table } from "@quillsql/react"; const MyTable = () => (
); ``` A simple component that displays the given data as a table. Make sure `QuillProvider` is a parent of the `Table` component. ## Examples [![Edit \[Ant Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/ant-design-quill-react-components-9qcs8k?embed=1) ```tsx theme={null} import { Table, useQuill } from "@quillsql/react"; import { AntTable } from "./ui/ant/Table"; export function AntDesignTable() { const report = useQuill("6644088e6e2470000cbdb109"); if (!report || !report.data) return null; return ; } ``` [![Edit \[Material Design\] Quill React Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/devbox/material-design-quill-react-components-d862dk?embed=1) ```tsx theme={null} import { Table, useQuill } from "@quillsql/react"; import { MaterialTable } from "./ui/ant/Table"; export function MaterialDesignTable() { const report = useQuill("6644088e6e2470000cbdb109"); if (!report || !report.data) return null; return (
); } ```
### Automatically fetch data by id If you know the id of the table you would like to display, you can pass in the reportId to the Table component and it will load and display the data for that table. ```jsx theme={null} import { QuillProvider, Table } from "@quillsql/react"; function App() { return (
); } ``` ### Pass data directly into the table Alternatively, if you have the actual data you would like to display (eg. you fetched the data using our `useQuill` hook) you can also pass in that data directly to the Table component and it will display that data without doing any async fetching. ```jsx Using raw data theme={null} import { QuillProvider, Table } from "@quillsql/react"; function App() { return (
); } ``` ```jsx Using useQuill theme={null} import { QuillProvider, Table } from "@quillsql/react"; import { MY_TABLE_ID } from "./constants"; function App() { const report = useQuill(MY_TABLE_ID); return (
); } ``` ## Props The table's id. The most usage is through a detail page built to navigate from the dashboard - using the onClick callback to get the report id, and navigating to a route (say, reports/:id) where the url param is passed in as the reportId. For a standalone table, you can find the reportId in the Quill BI Platform and pass it in directly. When a `reportId` is passed, the table will first fetch the data necessary to render this table, and then it will render the rows and columns that it receives from the server. A `reportId` must be passed if `rows` and `columns` are not present. The rows of the table to show, if any. When `rows` and `columns` are passed, the table will not refetch the given report and will instead simply render the rows and columns it was given. Both `rows` and `columns` must be passed if `reportId` is not present. The columns of the table to show, if any. When `rows` and `columns` are passed, the table will not refetch the given report and will instead simply render the rows and columns it was given. Both `rows` and `columns` must be passed if `reportId` is not present. The placeholder filename to use when downloading this table as a csv file. Whether to hide the download csv button. Whether this table component is loading. A callback that is fired when the user clicks download csv. A loading component to show when the table is loading. Styles the top-level container of the Table. This can be useful for TailwindCSS-style classname strings. The CSS styles that wrap the table. # Custom Themes Source: https://quill.co/docs/react/themes Customize your dashboard by passing a custom theme For pixel-perfect control over theming and styling, all Quill components accept custom components as props. See our [dashboard docs](/react/dashboard) for an example. ```tsx App.tsx theme={null} import { QuillProvider } from "@quillsql/react"; // You can a custom theme to be applied to the default Quill // components, or you can pass your own components with // pixel-perfect styling. const MY_CUSTOM_THEME = { fontFamily: "Inter; Helvetica", backgroundColor: "#FFFFFF", primaryTextColor: "#364153", secondaryTextColor: "#6C727F", chartLabelFontFamily: "Inter; Helvetica", chartLabelColor: "#666666", chartTickColor: "#CCCCCC", chartColors: ["#4E80EE", "#E14F62", "#55B5A6", "#E9A23B"], borderColor: "#E5E7EB", primaryButtonColor: "#364153", secondaryButtonColor: "#384151", borderWidth: 1, labelFontWeight: 500, fontSize: 14, loadingStateBackgroundColor: "#F9F9FA", hoverBackgroundColor: "#F4F4F5", }; function App() { return ( {children} ); } ``` ## QuillTheme The font family you want Quill to use. This is usually the same font your app uses. ex: "Inter" The default font size you want Quill to use. This is usually the same as the fontSize of your app (eg. 16 for "16px"). The background color you want Quill to use. This is usually the same background color your app uses. ex: "#FFFFFF" The background color you want Quill to use while hovering. The color you want Quill to use for primary text. This is usually the same primary text color color your app uses. ex: "#000000" The text color you want Quill to use for secondary text. This is usually the same secondary font color your app uses. ex: "#888888" The font family you want Quill to use for chart labels. This is usually the same as your base font family. ex: "Inter" The text color you want Quill to use for chart labels. This is usually the same as or similar to the secondary color your app uses. ex: "#666666" The color you want Quill to use for chart ticks. This is usually the same as or similar to the secondary color your app uses. ex: "#CCCCCC" The chart colors you want Quill to use. This is usually the same as the primary and secondary colors your app uses. You can pass in as many colors as you want, and you can also override these by passing in an array for the colors prop in the Chart component. ex: \['#6269E9', '#E14F62'] The border color you want Quill to use. This is usually the same as or similar to the border color your app uses. ex: "#CCCCCC" The border width you want Quill to use. This is usually the same as or similar to the border width your app uses. ex: 1 (ie. "1px") The color of the primary buttons you want Quill to use. Typically, you can just pass in a custom button with the styling you want instead of using this. The color of the secondary buttons you want Quill to use. Typically, you can just pass in a custom button with the styling you want instead of using this. The font weight you want Quill to use for buttons. Typically, you can just pass in a custom button with the styling you want instead of using this. (eg. 600\) The font weight you want Quill to use for labels. Typically, you can just pass in a custom label component with the styling you want instead of using this. (eg. 400) The background color you want Quill to use while in a loading state. This is usually the same as the background color you app uses (eg. '#FFFFFF'). # useDashboard Source: https://quill.co/docs/react/use-dashboard Add a dashboard to your app, styled with your existing UI components. Used alongside [useDashboardReport](/react/use-dashboard-report) for fully customizable dashboards. Below is an example with shadcn showing fully custom styling. ```tsx App.tsx theme={null} import { QuillProvider, useDashboard, useDashboardReport, StaticChart, } from "@quillsql/react"; import { Card, CardHeader } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; function App() { return ( ); } function CustomDashboard() { const { sections, isLoading } = useDashboard("quill demo dashboard"); return ( <> ); } function ChartsSection({ reports }: { reports: any[] }) { return (
{reports.map((report: any) => ( ))}
); } function ChartCard({ reportId, name }) { const { report, loading } = useDashboardReport(reportId); if (loading) { return (
); } return (
); } ``` 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 The dashboard sections containing reports, organized by section name The report's unique id The name of the report The name of the dashboard this report belongs to The rows of data returned from this report's query The columns data returned from this report's query The type of this chart The table and field this report uses for date filtering, if any The pivot used in this query, if any The label of the xAxis The field to use for this report's xAxis The format for this report's xAxis The template flag for a report A list of metadata about the yAxes of this report The relative ordering of this report in relation to its siblings. Ordering starts at 1 and counts up (eg. 1, 2, 3, etc.). Reports in the same section are first grouped by chartType and then each group is sorted by order. The rows of data returned from this report's query over the comparison date range as opposed to the primary date range An array filters that have been applied to this query A page prop used for smart table pagination A sort prop used for smart table pagination A total row count used for SQL pagination A query for the report used by the report builder on initial load of report A map of dashboard filters to the appropriate table and field for the report A list of reference lines to render on compatible charts Resolved reference line y values A flag to determine whether to automatically display custom fields Columns with custom fields The pivot row info A list of metadata about the pivot yAxes of this report Total pivot row count Available filters for the dashboard. These are typically rendered with html select or similar UI components. Function to apply filters to the dashboard # useDashboardReport Source: https://quill.co/docs/react/use-dashboard-report Add a dashboard to your app, styled with your existing UI components. Used alongside [useDashboard](/react/use-dashboard) for fully customizable dashboards. This hook exists to abstract report specific logic to report specific components and to allow parallel fetching of queries in a dashboard. Below is an example with shadcn showing fully custom styling. ```tsx App.tsx theme={null} import { QuillProvider, useDashboard, useDashboardReport, format, } from "@quillsql/react"; import { Card, CardHeader } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; function App() { return ( ); } function CustomDashboard() { const { sections, isLoading } = useDashboard("quill demo dashboard"); return ( <> ); } function MetricsSection({ reports }: { reports: any[] }) { return (
{reports.map((report: any) => ( ))}
); } interface MetricCardProps { reportId: string; name: string; } export function MetricCard({ reportId, name }: MetricCardProps) { const { report, loading } = useDashboardReport(reportId); if (loading) { return (

{name}

); } if (!report) return null; const mainValue = format({ value: report.pivotRows?.[0]?.[report.xAxisField], format: report.xAxisFormat, }); return (

{mainValue}

{name}

); } ``` Parameters The Quill reportId of the report to load Returns The report data returned by useDashboardReport. The report's unique id. The name of the report. The name of the dashboard this report belongs to. The rows of data returned from this report's query. The columns data returned from this report's query. The type of this chart. The field to use for this report's xAxis. The format for this report's xAxis. Whether the report data is currently loading Function to apply filters to the report. Function to delete the report. Function to fetch the next page of data. Note that the pageSize is set in useDashboard. # useDashboards Source: https://quill.co/docs/react/use-dashboards Get a list of all Quill dashboards. ```tsx theme={null} import { useDashboards } from "@quillsql/react"; export const DashboardList = () => { const { dashboards, isLoading } = useDashboards(); return (
{isLoading ? (
Loading...
) : ( dashboards.map((dashboard) => (
{dashboard.name}
)) )}
); }; ``` Can be used alongside [useDashboard](/react/use-dashboard-report) for a fully dynamic directory of dashboards. ```tsx theme={null} import { useDashboards, useDashboard, useDashboardReport, StaticChart, } from "@quillsql/react"; import { Card } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; export const DashboardList = () => { const { dashboards, isLoading } = useDashboards(); return (
{isLoading ? (
Loading...
) : ( dashboards.map((dashboard) => (
)) )}
); }; function CustomDashboard({ name }) { const { sections, isLoading } = useDashboard(name); if (isLoading) return
Loading...
; return ( <> {Object.entries(sections).map(([sectionName, reports]) => ( ))} ); } function ReportsSection({ sectionName, reports, }: { sectionName: string; reports: any[]; }) { return (

{sectionName}

{reports.map((report: any) => ( ))}
); } interface ReportCardProps { reportId: string; name: string; } export function ReportCard({ reportId, name }: ReportCardProps) { const { report, loading } = useDashboardReport(reportId); if (loading) { return ( ); } return ( ); } ``` Returns The list of dashboards returned by useDashboards. The unique dashboard ID. The dashboard name. Whether the dashboards are currently loading. # useExport Source: https://quill.co/docs/react/use-export A simple way to export data from Quill ```tsx App.tsx theme={null} import { QuillProvider, useExport } from "@quillsql/react"; function CustomComponent() { // pass in any report created in the Quill BI Platform const { downloadCSV } = useExport(QUILL_ID); return ; } function App() { return ( ); } ``` Make sure `QuillProvider` is a parent of the component using the `useExport` hook. The id of the report you created in the Quill BI Platform. Don't have an id yet? Learn how to [create a report](/bi-platform/report) in the Quill BI Platform to get started. # useQuill Source: https://quill.co/docs/react/use-quill A pragmatic data API for your data ```tsx App.tsx theme={null} import { QuillProvider, useQuill } from "@quillsql/react"; function CustomComponent() { // pass in any report created in the Quill BI Platform const report = useQuill(QUILL_ID); return
{JSON.stringify(report, null, 2)}
; } function App() { return ( ); } ``` ```json Sample Response theme={null} { data: { name: "My Report", rows: [...], columns: [...], chartType: "table", fields: [...], xAxisField: "created_at", yAxisField: "amount", xAxisLabel: "Created" yAxisLabel: "Total Amount", queryString: "SELECT * FROM transactions;", }, loading: false, error: null } ``` Make sure `QuillProvider` is a parent of the component using the `useQuill` hook. ## Props The id of the report you created in the Quill BI Platform. Don't have an id yet? Learn how to [create a chart](/bi-platform/report) in the Quill BI Platform to get started. # Quickstart Source: https://quill.co/docs/server/quickstart Get up and running with Quill on your own server in 10 minutes ## 1. Install the Quill SDK ```bash Node.js theme={null} npm install @quillsql/node ``` ```bash Python theme={null} pip3 install quillsql ``` ```bash Go theme={null} go get https://github.com/quill-sql/quill-go ``` ```bash PHP theme={null} composer require quill.co/quill-php ``` ```bash Ruby theme={null} bundle install quill-sql ``` ## 2. Create a new endpoint Instantiate `Quill` with your credentials and add the below `POST` endpoint. This example assumes you have an organization id on the user returned by your auth middleware. Queries will not work properly without the organization id. ```js Node theme={null} import { Quill } from "@quillsql/node"; const quill = new Quill({ privateKey: process.env.QULL_PRIVATE_KEY, databaseConnectionString: process.env.POSTGRES_READ, databaseType: "postgresql", }); // "authenticateJWT" is your own pre-existing auth middleware app.post("/quill", authenticateJWT, async (req, res) => { // assuming user fetched via auth middleware has an userId const { userId } = req.user; const { metadata } = req.body; const result = await quill.query({ tenants: [{ tenantField: "user_id", tenantIds: [userId] }] metadata, }); res.send(result); }); ``` ```python Python theme={null} from quillsql import Quill quill = Quill( private_key=os.getenv("QULL_PRIVATE_KEY"), database_connection_string=os.getenv("POSTGRES_READ"), database_type="postgresql" ) security = HTTPBearer() async def authenticate_jwt(token: str = Depends(security)): # Your JWT validation logic here # Return user object or raise HTTPException user = validate_jwt_token(token.credentials) return user @app.post("/quill") async def quill_post(data: Request, user: dict = Depends(authenticate_jwt)): # assuming user fetched via auth middleware has an userId user_id = user["user_id"] body = await data.json() metadata = body.get("metadata") result = quill.query( tenants=[{"tenantField": "user_id", "tenantIds": [user_id]}], metadata=metadata ) return result ``` ```go Go theme={null} import (     "github.com/quill-sql/quill-go" ) client := quill.NewClient(quill.ClientParams{ PrivateKey: os.Getenv("QUILL_PRIVATE_KEY"), DatabaseConnectionString: os.Getenv("POSTGRES_READ") }) // Add an endpoint http.HandleFunc("/quill", func(w http.ResponseWriter, r *http.Request) {     // fetch organizationID from your existing auth middleware    organizationID, _ := r.Context().Value(OrganizationIDContextKey).(string)     // Convert json body.metadata to RequestMetadata     body := &quill.RequestBody{}     err := json.NewDecoder(r.Body).Decode(body)     if err != nil {         http.Error(w, err.Error(), http.StatusBadRequest)         return     }     result, err := client.Query(organizationID, body.Metadata)     if err != nil {        http.Error(w, err.Error(), http.StatusBadRequest)        return     }     w.Header().Set("Content-Type", "application/json")     json.NewEncoder(w).Encode(result) }) ``` ```php PHP theme={null} $data['metadata'], 'orgId' => $orgId ]; $response = $quill->query($params); header('Content-Type: application/json'); $body = json_encode($response, JSON_PRETTY_PRINT); echo $body; exit; } ``` ```ruby Ruby theme={null} require "quill-sql" quill = Quill.new( private_key: ENV["PRIVATE_KEY"], database_connection_string: ENV["DB_URL"], database_type: "clickhouse" ) post "/quill" do # Assuming user fetched via auth middleware has a user_id org_id = request.env["org_id"] metadata = JSON.parse(request.body.read)["metadata"] result = quill.query( tenants: [{ tenantField: "org_id", tenantIds: [org_id] }], metadata: metadata ) result.to_json end ``` ## 3. Connect the frontend You connect Quill to React with the `QuillProvider` component. Similar to React's `Context.Provider`, `QuillProvider` wraps your React app and places Quill Client on the context, enabling you to access it from anywhere in your component tree. In App.js, let's wrap our React app with an `QuillProvider`. We suggest putting the `QuillProvider` somewhere high in your app, above any component that might need to access Quill data. ```js App.js theme={null} import { QuillProvider } from "@quillsql/react"; import Routes from "./Routes"; import UserContext from "./UserContext"; function App() { // Use your existing auth and user context const [user] = useContext(UserContext); return ( ); } ``` See the `QuillProvider` [API docs](/react/quill-provider) for more information.