We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nesirat/MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
{% extends "base.html" %}
{% block title %}Tickets{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Tickets</h1>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createTicketModal">
<i class="bi bi-plus-lg"></i> New Ticket
</button>
</div>
<!-- Filters -->
<div class="card mb-4">
<div class="card-body">
<form id="filterForm" class="row g-3">
<div class="col-md-4">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="">All</option>
<option value="open">Open</option>
<option value="in_progress">In Progress</option>
<option value="resolved">Resolved</option>
<option value="closed">Closed</option>
</select>
</div>
<div class="col-md-4">
<label for="priority" class="form-label">Priority</label>
<select class="form-select" id="priority" name="priority">
<option value="">All</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-primary">Apply Filters</button>
</div>
</form>
</div>
</div>
<!-- Tickets Table -->
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Subject</th>
<th>Status</th>
<th>Priority</th>
<th>Created</th>
<th>Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="ticketsTableBody">
<!-- Tickets will be loaded here -->
</tbody>
</table>
</div>
<!-- Pagination -->
<nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center" id="pagination">
<!-- Pagination will be loaded here -->
</ul>
</nav>
</div>
</div>
</div>
<!-- Create Ticket Modal -->
<div class="modal fade" id="createTicketModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create New Ticket</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="createTicketForm">
<div class="mb-3">
<label for="subject" class="form-label">Subject</label>
<input type="text" class="form-control" id="subject" name="subject" required>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="4" required></textarea>
</div>
<div class="mb-3">
<label for="priority" class="form-label">Priority</label>
<select class="form-select" id="priority" name="priority" required>
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="createTicketBtn">Create Ticket</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
let currentPage = 1;
const pageSize = 10;
let currentFilters = {};
// Load tickets
async function loadTickets(page = 1) {
const params = new URLSearchParams({
page: page,
size: pageSize,
...currentFilters
});
try {
const response = await api.get(`/api/v1/tickets?${params}`);
const data = await response.json();
// Update table
const tbody = document.getElementById('ticketsTableBody');
tbody.innerHTML = data.tickets.map(ticket => `
<tr>
<td>${ticket.id}</td>
<td>${ticket.subject}</td>
<td><span class="badge bg-${getStatusColor(ticket.status)}">${ticket.status}</span></td>
<td><span class="badge bg-${getPriorityColor(ticket.priority)}">${ticket.priority}</span></td>
<td>${formatDate(ticket.created_at)}</td>
<td>${formatDate(ticket.updated_at)}</td>
<td>
<a href="/tickets/${ticket.id}" class="btn btn-sm btn-primary">
<i class="bi bi-eye"></i>
</a>
</td>
</tr>
`).join('');
// Update pagination
updatePagination(data.total, page, pageSize);
} catch (error) {
showMessage('Error loading tickets', 'danger');
}
}
// Handle filter form submission
document.getElementById('filterForm').addEventListener('submit', function(e) {
e.preventDefault();
currentFilters = {
status: document.getElementById('status').value,
priority: document.getElementById('priority').value
};
currentPage = 1;
loadTickets(currentPage);
});
// Handle create ticket
document.getElementById('createTicketBtn').addEventListener('click', async function() {
const form = document.getElementById('createTicketForm');
const formData = new FormData(form);
try {
const response = await api.post('/api/v1/tickets', {
subject: formData.get('subject'),
description: formData.get('description'),
priority: formData.get('priority')
});
if (response.ok) {
const modal = bootstrap.Modal.getInstance(document.getElementById('createTicketModal'));
modal.hide();
form.reset();
loadTickets(currentPage);
showMessage('Ticket created successfully', 'success');
}
} catch (error) {
showMessage('Error creating ticket', 'danger');
}
});
// Helper functions
function getStatusColor(status) {
const colors = {
'open': 'primary',
'in_progress': 'warning',
'resolved': 'success',
'closed': 'secondary'
};
return colors[status] || 'secondary';
}
function getPriorityColor(priority) {
const colors = {
'low': 'info',
'medium': 'primary',
'high': 'warning',
'critical': 'danger'
};
return colors[priority] || 'secondary';
}
function updatePagination(total, currentPage, pageSize) {
const totalPages = Math.ceil(total / pageSize);
const pagination = document.getElementById('pagination');
let html = '';
// Previous button
html += `
<li class="page-item ${currentPage === 1 ? 'disabled' : ''}">
<a class="page-link" href="#" data-page="${currentPage - 1}">Previous</a>
</li>
`;
// Page numbers
for (let i = 1; i <= totalPages; i++) {
html += `
<li class="page-item ${i === currentPage ? 'active' : ''}">
<a class="page-link" href="#" data-page="${i}">${i}</a>
</li>
`;
}
// Next button
html += `
<li class="page-item ${currentPage === totalPages ? 'disabled' : ''}">
<a class="page-link" href="#" data-page="${currentPage + 1}">Next</a>
</li>
`;
pagination.innerHTML = html;
// Add click handlers
pagination.querySelectorAll('.page-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const page = parseInt(this.dataset.page);
if (page >= 1 && page <= totalPages) {
currentPage = page;
loadTickets(currentPage);
}
});
});
}
// Initial load
loadTickets(currentPage);
});
</script>
{% endblock %}