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