Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.veloiq.dev/llms.txt

Use this file to discover all available pages before exploring further.

Three provider objects exported from @veloiq/ui connect your React application to the VeloIQ backend: authProvider handles JWT login and session management, accessControlProvider reads role permissions and hides or disables unauthorized UI elements, and httpClient is an Axios instance that attaches the JWT to every API request. All three implement the corresponding Refine provider interfaces and are passed directly to the <Refine> component.

authProvider

authProvider implements Refine’s AuthProvider interface. It communicates with the VeloIQ backend’s /auth/ endpoints and stores credentials in localStorage.
MethodBehaviour
login({ username, password })Posts to POST /auth/login. On success, stores the access token under the key jm_access_token and the user profile under jm_user. Also fetches and caches role permissions (/auth/roles) and resource permissions (/auth/resource-permissions) for the access control provider. Redirects to / on success.
logout()Removes jm_access_token, jm_user, jm_role_permissions, and jm_resource_permissions from localStorage. Redirects to /login.
check()Returns { authenticated: true } when jm_access_token is present; otherwise redirects to /login.
getIdentity()Returns the cached user from localStorage, or fetches it from GET /auth/me using the stored token.
getPermissions()Returns the roles array from the cached user profile.
onError(error)Triggers logout and redirects to /login on HTTP 401.

accessControlProvider

accessControlProvider implements Refine’s AccessControlProvider interface. It mirrors the backend’s three-layer permission model entirely in the browser using cached data from localStorage — no additional network request is made per permission check.

Permission layers

LayerSourceDescription
1 — Global rolesjm_role_permissions in localStorageActions each role may perform across all resources. Fetched from GET /auth/roles at login. Falls back to built-in Admin/Manager/Viewer defaults when absent.
2 — Model-level exceptionsjm_resource_permissions in localStoragePer-resource action overrides set by @model_access on backend models. Fetched from GET /auth/resource-permissions at login.
3 — Field-levelreadRoles/writeRoles in FieldDefHandled by CanAccess wrappers in the DynamicResource components. Not passed through accessControlProvider.can().

Built-in fallback permissions

When no cached permissions are available (e.g. before the first login), the provider falls back to:
RoleAllowed actions
Adminlist, show, create, edit, delete, clone, field
Managerlist, show, create, edit, clone, field
Viewerlist, show, field
Admin users bypass all permission checks — the can function returns { can: true } immediately for any user whose roles include "admin" (case-insensitive).
The provider is configured with buttons.hideIfUnauthorized: true, so Refine automatically hides action buttons the current user cannot access.

httpClient

httpClient is a pre-configured Axios instance with a request interceptor that reads jm_access_token from localStorage and sets the Authorization: Bearer <token> header on every outgoing request.
import { httpClient } from "@veloiq/ui";

// Use directly for custom API calls
const response = await httpClient.get("/custom-endpoint");
Pass httpClient to @refinedev/simple-rest’s data provider to ensure all Refine data operations are authenticated:
import dataProvider from "@refinedev/simple-rest";
import { httpClient, API_URL } from "@veloiq/ui";

dataProvider(API_URL, httpClient)

API_URL

API_URL is the base URL constant used by the providers and the data provider. Its default value is "/api".
export const API_URL = "/api";
To change the base URL, configure a proxy in your Vite config to forward /api requests to the backend, or replace API_URL with your own constant:
// vite.config.ts
export default {
    server: {
        proxy: {
            "/api": { target: "http://localhost:8000", rewrite: (path) => path.replace(/^\/api/, "") },
        },
    },
};

LoginPage

LoginPage is the pre-built login page component. It renders a username/password form and delegates to authProvider.login().
import { LoginPage } from "@veloiq/ui";

<Route path="/login" element={<LoginPage appTitle="My App" />} />

Wiring providers in App.tsx

The following is the complete provider setup from the task-manager sample application.
import { Refine, Authenticated } from "@refinedev/core";
import { notificationProvider } from "@refinedev/antd";
import { BrowserRouter, Routes, Route, Outlet, Navigate } from "react-router-dom";
import routerProvider from "@refinedev/react-router-v6";
import dataProvider from "@refinedev/simple-rest";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import "@refinedev/antd/dist/reset.css";

import {
    authProvider,
    accessControlProvider,
    httpClient,
    LayoutWrapper,
    LoginPage,
    AllModelsProvider,
    DynamicList,
    DynamicShow,
    DynamicCreate,
    DynamicEdit,
    generateResources,
    API_URL,
    MultiPaneLayout,
    PrimaryShowContext,
    ColorModeContextProvider,
    authSystemModels,
} from "@veloiq/ui";
import type { PrimaryShowRendererProps } from "@veloiq/ui";
import { allModuleRegistrations, allSystemModels } from "./allModels.gen";

const PrimaryShowRenderer = ({ model, id, allModels }: PrimaryShowRendererProps) => (
    <DynamicShow model={model} allModels={allModels} idOverride={String(id)} />
);

const queryClient = new QueryClient();

export default function App() {
    const allModels = [...allSystemModels, ...authSystemModels];

    const resources = [
        ...allModuleRegistrations.flatMap(({ moduleName, models }) =>
            generateResources(models, moduleName)
        ),
        ...generateResources(authSystemModels, "access_control", { moduleLabel: "Access Control" }),
    ];

    return (
        <QueryClientProvider client={queryClient}>
            <AllModelsProvider models={allModels}>
                <BrowserRouter>
                    <Refine
                        dataProvider={dataProvider(API_URL, httpClient)}
                        authProvider={authProvider}
                        accessControlProvider={accessControlProvider}
                        routerProvider={routerProvider}
                        notificationProvider={notificationProvider}
                        resources={resources}
                        options={{ syncWithLocation: true, warnWhenUnsavedChanges: true }}
                    >
                        <ColorModeContextProvider>
                            <PrimaryShowContext.Provider value={PrimaryShowRenderer}>
                                <Routes>
                                    <Route path="/login" element={<LoginPage appTitle="Task Manager" />} />
                                    <Route
                                        element={
                                            <Authenticated key="auth" redirectOnFail="/login">
                                                <LayoutWrapper appTitle="Task Manager">
                                                    <Outlet />
                                                </LayoutWrapper>
                                            </Authenticated>
                                        }
                                    >
                                        {allSystemModels.map((model) => (
                                            <Route key={model.name} path={`/${model.resource || model.name}`}>
                                                <Route index element={
                                                    <MultiPaneLayout>
                                                        <DynamicList model={model} allModels={allModels} />
                                                    </MultiPaneLayout>
                                                } />
                                                <Route path="create" element={<DynamicCreate model={model} allModels={allModels} />} />
                                                <Route path="edit/:id" element={<DynamicEdit model={model} allModels={allModels} />} />
                                                <Route path="show/:id" element={
                                                    <MultiPaneLayout>
                                                        <DynamicShow model={model} allModels={allModels} />
                                                    </MultiPaneLayout>
                                                } />
                                            </Route>
                                        ))}
                                    </Route>
                                </Routes>
                            </PrimaryShowContext.Provider>
                        </ColorModeContextProvider>
                    </Refine>
                </BrowserRouter>
            </AllModelsProvider>
        </QueryClientProvider>
    );
}
Wrap the authenticated section in <Authenticated redirectOnFail="/login"> so Refine redirects unauthenticated users to the login page automatically. The authProvider.check() function drives this check.

UI Overview

All exports from @veloiq/ui at a glance.

DynamicResource

Schema-driven CRUD pages and the ModelDef type reference.