Skip to content

TQPro Component Library

SOURCE OF TRUTH for all repeating UI patterns in the admin portal. Before writing any HTML, find the component here and use the canonical snippet. If a pattern you need is not listed, add it here before using it. Last updated: 2026-04-02


Usage Rules

  1. Always use Bootstrap 5.3 utility classes for spacing, color, and display.
  2. Never hardcode colors — use CSS custom properties from tq-tokens.css.
  3. Always include data-* attributes shown in snippets — they are required by JS.
  4. Form inputs must always have an associated <label> with a matching for/id.
  5. Action buttons that trigger API calls must call tlinq().
  6. Loading states must use the TQ loading utilities from tqpro-components.js.
  7. Confirmations must use the TQ confirm dialog — never window.confirm().
  8. jQuery is available as $ — use it for DOM manipulation alongside vanilla JS.
  9. Always call escapeHtml() before inserting user-controlled data into DOM.
  10. Status badges use Bootstrap bg-* classes with Title Case labels.

Components


When to use: At the top of every page (except standalone pages like dashboards and download pages).

Dependencies: Bootstrap 5 breadcrumb component, tqadmin.css

HTML:

<nav class="tq-breadcrumb d-print-none" aria-label="breadcrumb">
    <ol class="breadcrumb mb-0">
        <li class="breadcrumb-item"><a href="index.html"><i class="bi bi-house-door"></i> Dashboard</a></li>
        <li class="breadcrumb-item"><a href="module-list.html">Module Name</a></li>
        <li class="breadcrumb-item active" aria-current="page">Current Page</li>
    </ol>
</nav>

Notes: - First item always has a bi bi-house-door icon (hub pages) or a module-specific icon (sub-pages linking back to module list) - Use d-print-none to hide in print - ol.breadcrumb.mb-0 — the margin comes from .tq-breadcrumb - Sub-pages use the module icon (e.g., bi bi-people for groups, bi bi-airplane for outbound) on the first breadcrumb item that links to the module list


When to use: Below the breadcrumb on every page to show the page title and primary action.

HTML:

<div class="d-flex align-items-center mb-3">
    <h4 class="mb-0"><i class="bi bi-icon-name me-2"></i> Page Title</h4>
    <div class="ms-auto d-flex gap-2">
        <a href="create-page.html" class="btn btn-primary btn-sm">
            <i class="bi bi-plus-lg"></i> New Item
        </a>
    </div>
</div>

Notes: - Always use h4.mb-0 for page titles - Icon before title text with me-2 spacing - Action buttons right-aligned via ms-auto - Use btn-primary for creation/navigation actions - Use btn-success for save/confirm/checkout actions - Use btn-outline-danger for destructive actions - Use btn-outline-secondary for cancel/back actions - btn-sm for buttons in headers and filter bars; default size for standalone actions


kpi-card

When to use: For summary statistics at the top of dashboards and list pages.

Variants: primary | success | warning | danger | info | secondary

HTML:

<div class="row g-3 mb-3">
    <div class="col-sm-6 col-lg-3">
        <div class="tq-kpi-card primary">
            <div>
                <div class="kpi-value">42</div>
                <div class="kpi-label">Active Bookings</div>
            </div>
        </div>
    </div>
    <!-- repeat for each KPI -->
</div>

Notes: - Border-left 4px accent with color variant class - Compact height — do not add extra padding or large font sizes - Use row g-3 grid with responsive column classes - Keep labels short (2-3 words max)


content-card

When to use: For page sections containing related data (tables, detail fields, form groups).

HTML:

<div class="content-card">
    <div class="content-card-header">
        <h5 class="content-card-title">
            <i class="bi bi-icon-name"></i> Section Title
        </h5>
        <button class="btn btn-primary btn-sm">
            <i class="bi bi-plus-lg"></i> Add Item
        </button>
    </div>
    <div class="content-card-body">
        <!-- section content -->
    </div>
</div>

Notes: - No hover animations (transform effects are suppressed) - Header has gradient background and flex layout - Action buttons in header use btn-sm - For cards wrapping tables, use content-card-body with p-0 and place the table directly inside


data-table

When to use: For any tabular data display.

HTML:

<div class="table-responsive">
    <table class="table table-hover table-sm align-middle">
        <thead class="table-light">
            <tr>
                <th>Column 1</th>
                <th>Column 2</th>
                <th class="text-end">Actions</th>
            </tr>
        </thead>
        <tbody id="tableBody">
            <!-- rows rendered by JS -->
        </tbody>
    </table>
</div>

Notes: - Always use table table-hover table-sm align-middle - Always wrap in div.table-responsive - Always use thead.table-light - Action column is always the last column, right-aligned with text-end - Empty state: <tr><td colspan="N" class="text-center text-muted py-3">No items found.</td></tr>


table-action-buttons

When to use: In the last column of data table rows for row-level actions.

HTML:

<td class="text-end">
    <button class="btn btn-sm btn-outline-primary" title="Edit">
        <i class="bi bi-pencil"></i>
    </button>
    <button class="btn btn-sm btn-outline-danger" title="Delete">
        <i class="bi bi-trash"></i>
    </button>
</td>

Notes: - Icon-only buttons with title attribute for accessibility - Standard icons: bi-pencil (edit), bi-trash (delete), bi-eye (view), bi-download (download) - Use btn-outline-primary for non-destructive actions - Use btn-outline-danger for destructive actions - For clickable rows (navigation), omit the action column and use style="cursor:pointer" on <tr>


status-badge

When to use: To display the status of any entity (booking, group, sale, ticket, etc.).

HTML:

<span class="badge bg-success">Confirmed</span>

Variants (Bootstrap bg-* classes): | Status type | CSS class | Example statuses | |-------------|-----------|------------------| | Positive | bg-success | Confirmed, Active, Available, Approved | | Warning | bg-warning text-dark | Pending, Option Held, Reserved | | Negative | bg-danger | Cancelled, Rejected, Expired | | Neutral | bg-secondary | Draft, Archived, Inactive | | Info | bg-primary | Enquiry, Quoted, In Progress | | Final | bg-dark | Completed, Departed | | Info-light | bg-info text-dark | Amended, Info |

Notes: - Always use Title Case labels ("Option Held", not "OPTION_HELD") - Use formatStatusLabel(status) from tqpro-components.js to convert raw status values - Never use custom CSS classes for badges — Bootstrap bg-* classes only - Add text-dark to light backgrounds (bg-warning, bg-info) for readability


filter-bar

When to use: Above data tables on hub/list pages for search and filtering.

HTML:

<div class="tq-filter-bar d-flex align-items-center gap-2 flex-wrap d-print-none">
    <input type="text" class="form-control form-control-sm" placeholder="Search..." style="max-width: 250px;">
    <select class="form-select form-select-sm" style="width: auto;">
        <option value="">All Statuses</option>
    </select>
    <button class="btn btn-primary btn-sm"><i class="bi bi-search"></i> Search</button>
    <button class="btn btn-outline-secondary btn-sm"><i class="bi bi-x-lg"></i> Clear</button>
    <a href="create.html" class="btn btn-primary btn-sm ms-auto">
        <i class="bi bi-plus-lg"></i> New Item
    </a>
</div>

Notes: - d-flex align-items-center gap-2 flex-wrap for layout - d-print-none to hide in print - Search input limited width via style="max-width: 250px" - Select dropdowns use width: auto to fit content - Primary action button right-aligned via ms-auto - Use explicit Search button (not auto-search on keyup) for consistency


empty-state

When to use: When a page or section has no data to display.

Variants: Page-level (full empty state) | Table-level (colspan row)

HTML (page-level):

<div class="tq-empty-state d-none" id="emptyState">
    <i class="bi bi-inbox display-1"></i>
    <h4>No Items Found</h4>
    <p>Get started by creating your first item.</p>
    <a href="create.html" class="btn btn-primary">
        <i class="bi bi-plus-lg"></i> Create Item
    </a>
</div>

HTML (table-level):

<tr>
    <td colspan="6" class="text-center text-muted py-3">No items found.</td>
</tr>

Notes: - Page-level: uses .tq-empty-state class, hidden by default with d-none, toggled via JS - Table-level: simple colspan row, always py-3 padding - Use Bootstrap Icons (bi-*) for the illustration icon, display-1 size


form-group

When to use: For every form field.

HTML:

<div class="mb-3">
    <label for="fieldName" class="form-label">Field Label <span class="text-danger">*</span></label>
    <input type="text" class="form-control" id="fieldName" required>
    <div class="invalid-feedback">This field is required.</div>
</div>

Notes: - Label always above input (not inline) - Required indicator: <span class="text-danger">*</span> after label text - Use Bootstrap's form-control, form-select, form-check classes - Validation via is-invalid class + invalid-feedback div - Grid layout: row g-3 to space multiple fields - Modal footer: include <small class="text-muted me-auto">* Required</small>


loading-button

When to use: When a button triggers an async operation.

Dependencies: tqpro-components.js

JavaScript:

import { loading } from './tqpro-components.js';

// Option 1: Wrap a promise (recommended)
loading.wrap(btn, tlinq('api/endpoint', data).then(result => {
    notify.success('Done');
}));

// Option 2: Manual control
loading.button(btn);
try {
    const result = await tlinq('api/endpoint', data);
    notify.success('Done');
} finally {
    loading.buttonReset(btn);
}

Notes: - Never manually replace button HTML with spinner — use loading.button()/loading.buttonReset() - loading.wrap() handles both loading state and reset automatically - Button is disabled during loading, original text is preserved and restored


toast-notification

When to use: To show feedback after an operation completes.

Dependencies: tqpro-components.js

JavaScript:

import { notify } from './tqpro-components.js';

notify.success('Booking created successfully');
notify.error('Failed to save changes');
notify.warning('Some items could not be processed');
notify.info('Export is being prepared...');

Notes: - Auto-dismiss: success (4s), warning (5s), error (6s), info (4s) - Position: fixed top-right - Never use alert() or custom toast implementations


confirm-dialog

When to use: Before any destructive action (delete, cancel, overwrite).

Dependencies: tqpro-components.js

JavaScript:

import { confirm } from './tqpro-components.js';

const confirmed = await confirm('Delete Booking', 'Are you sure? This cannot be undone.', {
    confirmText: 'Delete',
    confirmClass: 'btn-danger'
});
if (confirmed) {
    await tlinq('blm/booking/delete', { bookingId });
    notify.success('Booking deleted');
}

Notes: - Returns Promise<boolean> - Default confirm button is btn-primary with "OK" text - Override confirmText and confirmClass for destructive actions - Never use window.confirm()


Component Combinations

Filter bar + data table + paginator

tq-breadcrumb
page-header (h4 + action button)
tq-filter-bar (search + filters + create button)
table-responsive > table.table.table-hover.table-sm.align-middle > thead.table-light
pagination (TQ.pagination.render or renderSimple from tqpro-components.js)

KPI dashboard + card grid

tq-breadcrumb
row.g-3 > tq-kpi-card (3-4 cards across)
content-card > content-card-header + content-card-body
data display (table, card grid, or detail fields)

Form section with validation

content-card
content-card-header (section title)
content-card-body > form
row.g-3 > form-groups (label + input + invalid-feedback)
d-flex.gap-2.mt-3 (save btn-success + cancel btn-outline-secondary)
modal > modal-dialog > modal-content
modal-header (title)
modal-body > form > row.g-3 > form-groups
modal-footer (small.text-muted.me-auto "* Required" + btn-secondary Cancel + btn-primary Save)