TQPro Page Templates¶
These are the structural skeletons for every page type in the admin portal. Start every new page from one of these templates. Replace
<!-- slot: name -->comments with the appropriate components fromtq-components.md. Last updated: 2026-04-02
Template Index¶
| ID | Name | Used for |
|---|---|---|
| T1 | Application shell | Outermost page wrapper (every page) |
| T2 | Hub — list/search | Booking list, hotel list, groups list, ticket inventory |
| T3 | Spoke — detail | Booking detail, group summary, hotel rooms |
| T4 | Spoke — form | Hotel edit, booking wizard, package edit |
| T5 | Dashboard | Main dashboard, module dashboards (tripmaker, tickets, cruise) |
T1 — Application Shell¶
The outermost wrapper provided by header_bootstrap.html. Every page includes this
via the data-load-template attribute. New pages never generate this — they only
produce content for the main container.
HTML skeleton:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Title — TQPro</title>
<!-- Bootstrap 5.3 (with SRI) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7icsOifkntC8ERbjT0jZjC7TkL/Cfynz9FQ"
crossorigin="anonymous">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous">
<!-- TQPro shared styles -->
<link rel="stylesheet" href="css/tqapp.css">
<link rel="stylesheet" href="css/tqadmin.css">
<!-- Module-specific styles -->
<link rel="stylesheet" href="css/MODULE.css">
<!-- Icon libraries -->
<link rel="stylesheet" href="css/fontawesome.min.css">
<link rel="stylesheet" href="css/solid.css">
</head>
<body style="visibility:hidden">
<!-- Shell: loaded dynamically -->
<header data-load-template="header_bootstrap.html"></header>
<!-- Page content -->
<div class="container-fluid" style="margin-top: 80px;">
<!-- slot: page-content -->
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- Page module -->
<script type="module">
import { setGlobalHandlers } from './js/modules/globals.js';
import { initializePage } from './js/modules/MODULE.js';
setGlobalHandlers({ requireAuth: true });
initializePage();
</script>
</body>
</html>
Rules:
- The shell (header_bootstrap.html) is never modified per-page
- New pages only produce content inside <!-- slot: page-content -->
- style="visibility:hidden" prevents FOUC — the shell JS sets it to visible after loading
- Always include SRI integrity hashes on CDN links
- CSS load order: bootstrap → bootstrap-icons → tqapp → tqadmin → module CSS → icons
- jQuery is loaded globally and available as $
T2 — Hub page (list/search)¶
Used for pages that list, search, and filter collections of entities.
Typical component slots:
- slot: breadcrumb → tq-breadcrumb
- slot: kpi-cards → tq-kpi-card grid (optional)
- slot: filter-bar → tq-filter-bar
- slot: data-table → table.table.table-hover.table-sm.align-middle in table-responsive
- slot: empty-state → tq-empty-state
- slot: paginator → TQ.pagination.render() (JS-driven)
HTML skeleton:
<div class="container-fluid" style="margin-top: 80px;">
<!-- slot: breadcrumb -->
<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 active" aria-current="page">Items</li>
</ol>
</nav>
<!-- slot: kpi-cards (optional) -->
<div class="row g-3 mb-3" id="kpiCards">
<!-- tq-kpi-card instances -->
</div>
<!-- slot: filter-bar -->
<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" id="searchInput"
placeholder="Search..." style="max-width: 250px;">
<select class="form-select form-select-sm" id="statusFilter" style="width: auto;">
<option value="">All Statuses</option>
</select>
<button class="btn btn-primary btn-sm" id="btnSearch">
<i class="bi bi-search"></i> Search
</button>
<a href="create.html" class="btn btn-primary btn-sm ms-auto">
<i class="bi bi-plus-lg"></i> New Item
</a>
</div>
<!-- slot: data-table -->
<div class="table-responsive">
<table class="table table-hover table-sm align-middle">
<thead class="table-light">
<tr>
<th>Name</th>
<th>Status</th>
<th>Date</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody id="tableBody">
<!-- JS-rendered rows -->
</tbody>
</table>
</div>
<!-- slot: empty-state -->
<div class="tq-empty-state d-none" id="emptyState">
<i class="bi bi-inbox display-1"></i>
<h4>No Items Found</h4>
<p>Try adjusting your search criteria.</p>
</div>
<!-- slot: paginator -->
<div id="pagination"></div>
</div>
T3 — Spoke page (detail / read-only)¶
Used for pages that display a single entity with multiple sections.
Typical component slots:
- slot: breadcrumb → tq-breadcrumb (3+ levels)
- slot: page-header → h4 with icon + action buttons
- slot: summary-section → content-card with key fields
- slot: tabs or sections → nav-tabs or stacked content-card sections
- slot: related-data → data-table inside content-card
HTML skeleton:
<div class="container-fluid" style="margin-top: 80px;">
<!-- slot: breadcrumb -->
<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="list.html">Items</a></li>
<li class="breadcrumb-item active" aria-current="page">Item Detail</li>
</ol>
</nav>
<!-- slot: page-header -->
<div class="d-flex align-items-center mb-3">
<h4 class="mb-0"><i class="bi bi-file-text me-2"></i> <span id="pageTitle">Item Name</span></h4>
<div class="ms-auto d-flex gap-2">
<button class="btn btn-outline-primary btn-sm" id="btnEdit">
<i class="bi bi-pencil"></i> Edit
</button>
<button class="btn btn-outline-danger btn-sm" id="btnDelete">
<i class="bi bi-trash"></i> Delete
</button>
</div>
</div>
<!-- slot: summary-section -->
<div class="content-card">
<div class="content-card-header">
<h5 class="content-card-title"><i class="bi bi-info-circle"></i> Overview</h5>
</div>
<div class="content-card-body">
<!-- detail fields -->
</div>
</div>
<!-- slot: related-data -->
<div class="content-card">
<div class="content-card-header">
<h5 class="content-card-title"><i class="bi bi-list"></i> Related Items</h5>
<button class="btn btn-primary btn-sm"><i class="bi bi-plus-lg"></i> Add</button>
</div>
<div class="content-card-body p-0">
<div class="table-responsive">
<table class="table table-hover table-sm align-middle mb-0">
<!-- ... -->
</table>
</div>
</div>
</div>
</div>
T4 — Spoke page (form / edit)¶
Used for pages that create or edit an entity.
HTML skeleton:
<div class="container-fluid" style="margin-top: 80px;">
<!-- slot: breadcrumb -->
<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="list.html">Items</a></li>
<li class="breadcrumb-item active" aria-current="page">Edit Item</li>
</ol>
</nav>
<!-- slot: page-header -->
<div class="d-flex align-items-center mb-3">
<h4 class="mb-0"><i class="bi bi-pencil-square me-2"></i> Edit Item</h4>
<div class="ms-auto d-flex gap-2">
<button class="btn btn-success" id="btnSave">
<i class="bi bi-check-lg"></i> Save
</button>
<a href="list.html" class="btn btn-outline-secondary">Back</a>
</div>
</div>
<!-- slot: form-sections -->
<div class="content-card">
<div class="content-card-header">
<h5 class="content-card-title"><i class="bi bi-info-circle"></i> Basic Information</h5>
</div>
<div class="content-card-body">
<form id="editForm">
<div class="row g-3">
<div class="col-md-6">
<label for="name" class="form-label">Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" required>
<div class="invalid-feedback">Name is required.</div>
</div>
<div class="col-md-6">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status">
<option value="DRAFT">Draft</option>
<option value="ACTIVE">Active</option>
</select>
</div>
</div>
</form>
</div>
</div>
</div>
Rules:
- Save button uses btn-success (commit action)
- Save button in page header, not at bottom of form (this is the admin pattern)
- Never put the save button inside the form — it's in the page header
- On mobile, action buttons wrap naturally via flex
T5 — Dashboard / overview¶
Used for module entry points that show KPIs and quick-access tiles.
HTML skeleton:
<div class="container-fluid" style="margin-top: 80px;">
<!-- slot: breadcrumb -->
<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 active" aria-current="page">Module Dashboard</li>
</ol>
</nav>
<!-- slot: kpi-cards -->
<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" id="kpiTotal">-</div>
<div class="kpi-label">Total Items</div>
</div>
</div>
</div>
<!-- more KPI cards -->
</div>
<!-- slot: filter-bar -->
<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</option>
</select>
<button class="btn btn-primary btn-sm ms-auto">
<i class="bi bi-plus-lg"></i> New Item
</button>
</div>
<!-- slot: content (card grid or table) -->
<div class="row g-3" id="contentGrid">
<!-- JS-rendered cards or table -->
</div>
<!-- slot: empty-state -->
<div class="tq-empty-state d-none" id="emptyState">
<i class="bi bi-inbox display-1"></i>
<h4>No Items Yet</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</a>
</div>
</div>
Layout Rules (apply to all templates)¶
- Content container is always
container-fluidwithmargin-top: 80px(navbar clearance) - Content max-width is 1400px, auto-centered (set globally in
tqadmin.css— do not add inline width/max-width styles) - Vertical spacing between page sections:
mb-3(cards, filter bars, KPI grids) - Cards use
1.25reminternal padding (.content-card-body) - Responsive breakpoints follow Bootstrap 5.3 (sm/md/lg/xl/xxl)
- All pages load the shared shell via
<header data-load-template="header_bootstrap.html"> - Pages hide initially (
visibility:hidden) to prevent FOUC - KPI cards use compact padding (
0.75rem 1rem) — low-height, non-intrusive - Breadcrumbs use compact padding (
0.5rem 1rem) with0.25remborder-radius - No hover zoom/transform animations on cards