Changed Client-side Search to Server-side Search

This commit is contained in:
2025-09-05 12:40:38 -04:00
parent e05434137c
commit 38ae4802b8
6 changed files with 117 additions and 22 deletions

View File

@ -227,6 +227,71 @@ td:nth-child(2) span {
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1); box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
} }
.search-container {
margin: 20px 0;
padding: 0 20px;
}
.search-form {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}
.search-box {
flex: 1;
min-width: 300px;
padding: 10px 15px;
border: 2px solid #ddd;
border-radius: 5px;
font-size: 16px;
transition: border-color 0.3s;
}
.search-box:focus {
outline: none;
border-color: #007cba;
}
.search-btn {
padding: 10px 20px;
background-color: #007cba;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}
.search-btn:hover {
background-color: #005a87;
}
.clear-search-btn {
padding: 10px 15px;
background-color: #6c757d;
color: white;
text-decoration: none;
border-radius: 5px;
font-size: 14px;
transition: background-color 0.3s;
}
.clear-search-btn:hover {
background-color: #545b62;
}
/* Show search results info */
.search-results-info {
margin: 10px 0;
padding: 10px;
background-color: #e7f3ff;
border-left: 4px solid #007cba;
border-radius: 4px;
}
/* Status Filter Dropdown */ /* Status Filter Dropdown */
.status-filter-container { .status-filter-container {
position: relative; position: relative;

View File

@ -15,7 +15,6 @@ document.addEventListener('DOMContentLoaded', function() {
if (isDashboard) { if (isDashboard) {
// Dashboard-specific initialization // Dashboard-specific initialization
initSearch();
initStatusFilter(); initStatusFilter();
initTableSorting(); initTableSorting();
@ -192,22 +191,6 @@ function initThemeToggle() {
document.body.appendChild(toggle); document.body.appendChild(toggle);
} }
function initSearch() {
const searchBox = document.createElement('input');
searchBox.type = 'text';
searchBox.placeholder = 'Search tickets...';
searchBox.className = 'search-box';
searchBox.oninput = (e) => {
const searchTerm = e.target.value.toLowerCase();
const rows = document.querySelectorAll('tbody tr');
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
};
document.querySelector('h1').after(searchBox);
}
function initStatusFilter() { function initStatusFilter() {
const filterContainer = document.createElement('div'); const filterContainer = document.createElement('div');
filterContainer.className = 'status-filter-container'; filterContainer.className = 'status-filter-container';

View File

@ -18,6 +18,7 @@ class DashboardController {
$sortDirection = isset($_GET['dir']) ? $_GET['dir'] : 'desc'; $sortDirection = isset($_GET['dir']) ? $_GET['dir'] : 'desc';
$category = isset($_GET['category']) ? $_GET['category'] : null; $category = isset($_GET['category']) ? $_GET['category'] : null;
$type = isset($_GET['type']) ? $_GET['type'] : null; $type = isset($_GET['type']) ? $_GET['type'] : null;
$search = isset($_GET['search']) ? trim($_GET['search']) : null; // ADD THIS LINE
// Handle status filtering // Handle status filtering
$status = null; $status = null;
@ -29,8 +30,8 @@ class DashboardController {
} }
// If $_GET['show_all'] exists or no status param with show_all, show all tickets (status = null) // If $_GET['show_all'] exists or no status param with show_all, show all tickets (status = null)
// Get tickets with pagination and sorting // Get tickets with pagination, sorting, and search
$result = $this->ticketModel->getAllTickets($page, $limit, $status, $sortColumn, $sortDirection, $category, $type); $result = $this->ticketModel->getAllTickets($page, $limit, $status, $sortColumn, $sortDirection, $category, $type, $search);
// Get categories and types for filters // Get categories and types for filters
$categories = $this->getCategories(); $categories = $this->getCategories();

View File

@ -35,7 +35,7 @@ class TicketModel {
return $comments; return $comments;
} }
public function getAllTickets($page = 1, $limit = 15, $status = 'Open', $sortColumn = 'ticket_id', $sortDirection = 'desc', $category = null, $type = null) { public function getAllTickets($page = 1, $limit = 15, $status = 'Open', $sortColumn = 'ticket_id', $sortDirection = 'desc', $category = null, $type = null, $search = null) {
// Calculate offset // Calculate offset
$offset = ($page - 1) * $limit; $offset = ($page - 1) * $limit;
@ -71,6 +71,14 @@ class TicketModel {
$paramTypes .= str_repeat('s', count($types)); $paramTypes .= str_repeat('s', count($types));
} }
// Search Functionality
if ($search && !empty($search)) {
$whereConditions[] = "(title LIKE ? OR description LIKE ? OR ticket_id LIKE ? OR category LIKE ? OR type LIKE ?)";
$searchTerm = "%$search%";
$params = array_merge($params, [$searchTerm, $searchTerm, $searchTerm, $searchTerm, $searchTerm]);
$paramTypes .= 'sssss';
}
$whereClause = ''; $whereClause = '';
if (!empty($whereConditions)) { if (!empty($whereConditions)) {
$whereClause = 'WHERE ' . implode(' AND ', $whereConditions); $whereClause = 'WHERE ' . implode(' AND ', $whereConditions);

View File

@ -76,7 +76,7 @@
<div class="ticket-footer"> <div class="ticket-footer">
<button type="submit" class="btn primary">Create Ticket</button> <button type="submit" class="btn primary">Create Ticket</button>
<button type="button" onclick="window.location.href='<?php echo $GLOBALS['config']['BASE_URL']; ?>'" class="btn back-btn">Cancel</button> <button type="button" onclick="window.location.href='<?php echo $GLOBALS['config']['BASE_URL']; ?>/'" class="btn back-btn">Cancel</button>
</div> </div>
</form> </form>
</div> </div>

View File

@ -17,7 +17,45 @@
<h1>Tinker Tickets</h1> <h1>Tinker Tickets</h1>
<button onclick="window.location.href='/ticket/create'" class="btn create-ticket">New Ticket</button> <button onclick="window.location.href='/ticket/create'" class="btn create-ticket">New Ticket</button>
</div> </div>
<div class="search-container">
<form method="GET" action="" class="search-form">
<!-- Preserve existing parameters -->
<?php if (isset($_GET['status'])): ?>
<input type="hidden" name="status" value="<?php echo htmlspecialchars($_GET['status']); ?>">
<?php endif; ?>
<?php if (isset($_GET['show_all'])): ?>
<input type="hidden" name="show_all" value="<?php echo htmlspecialchars($_GET['show_all']); ?>">
<?php endif; ?>
<?php if (isset($_GET['category'])): ?>
<input type="hidden" name="category" value="<?php echo htmlspecialchars($_GET['category']); ?>">
<?php endif; ?>
<?php if (isset($_GET['type'])): ?>
<input type="hidden" name="type" value="<?php echo htmlspecialchars($_GET['type']); ?>">
<?php endif; ?>
<?php if (isset($_GET['sort'])): ?>
<input type="hidden" name="sort" value="<?php echo htmlspecialchars($_GET['sort']); ?>">
<?php endif; ?>
<?php if (isset($_GET['dir'])): ?>
<input type="hidden" name="dir" value="<?php echo htmlspecialchars($_GET['dir']); ?>">
<?php endif; ?>
<input type="text"
name="search"
placeholder="Search tickets..."
class="search-box"
value="<?php echo isset($_GET['search']) ? htmlspecialchars($_GET['search']) : ''; ?>">
<button type="submit" class="search-btn">Search</button>
<?php if (isset($_GET['search']) && !empty($_GET['search'])): ?>
<a href="?" class="clear-search-btn">Clear</a>
<?php endif; ?>
</form>
</div>
<?php if (isset($_GET['search']) && !empty($_GET['search'])): ?>
<div class="search-results-info">
Showing results for: "<strong><?php echo htmlspecialchars($_GET['search']); ?></strong>"
(<?php echo $totalTickets; ?> ticket<?php echo $totalTickets != 1 ? 's' : ''; ?> found)
</div>
<?php endif; ?>
<div class="table-controls"> <div class="table-controls">
<div class="ticket-count"> <div class="ticket-count">
Total Tickets: <?php echo $totalTickets; ?> Total Tickets: <?php echo $totalTickets; ?>