Skip to content

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 from tq-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)

  1. Content container is always container-fluid with margin-top: 80px (navbar clearance)
  2. Content max-width is 1400px, auto-centered (set globally in tqadmin.css — do not add inline width/max-width styles)
  3. Vertical spacing between page sections: mb-3 (cards, filter bars, KPI grids)
  4. Cards use 1.25rem internal padding (.content-card-body)
  5. Responsive breakpoints follow Bootstrap 5.3 (sm/md/lg/xl/xxl)
  6. All pages load the shared shell via <header data-load-template="header_bootstrap.html">
  7. Pages hide initially (visibility:hidden) to prevent FOUC
  8. KPI cards use compact padding (0.75rem 1rem) — low-height, non-intrusive
  9. Breadcrumbs use compact padding (0.5rem 1rem) with 0.25rem border-radius
  10. No hover zoom/transform animations on cards