Ticket Management System
<!– –>
body {
background-color: #001f3f; /* Dark blue */
color: white;
font-family: “Segoe UI”, Tahoma, Geneva, Verdana, sans-serif;
}
h1, h2 {
font-weight: 600;
}
/* Status summary boxes */
.status-box {
border-radius: 8px;
padding: 1.5rem 1rem;
color: white;
text-align: center;
font-weight: 600;
cursor: pointer;
user-select: none;
transition: background-color 0.3s ease;
}
.status-open {
background-color: #dc3545; /* Bootstrap danger red */
color: black;
}
.status-closed {
background-color: #198754; /* Bootstrap green */
color: white;
}
.status-pending {
background-color: #fd7e14; /* Bootstrap orange */
color: black;
}
.status-box:hover {
filter: brightness(0.85);
}
.status-count {
font-size: 2.2rem;
margin-top: 0.25rem;
}
/* Ticket card */
.ticket-card {
background: #ffffff20; /* White with transparency */
border-radius: 6px;
border: 1px solid #444;
margin-bottom: 1rem;
box-shadow: 0 2px 4px rgb(0 0 0 / 0.1);
transition: box-shadow 0.2s ease-in-out;
color: white;
position: relative;
padding-bottom: 2.5rem;
}
.ticket-card:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.3);
}
.ticket-ribbon {
height: 6px;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
}
.ribbon-open {
background-color: #0d6efd; /* Bootstrap primary blue */
}
.ribbon-pending {
background-color: #ffc107; /* Bootstrap warning yellow */
}
.ribbon-closed {
background-color: #6c757d; /* Bootstrap secondary gray */
}
.ticket-body {
padding: 1rem 1.25rem;
}
.ticket-details {
white-space: pre-wrap; /* preserve line breaks */
margin-bottom: 0.75rem;
}
.edit-ticket-btn {
position: absolute;
bottom: 0.75rem;
right: 1rem;
font-size: 0.8rem;
}
/* Customer table adjustments */
.customer-name {
cursor: pointer;
color: #0d6efd;
text-decoration: underline;
}
.customer-name:hover {
color: #82b1ff;
}
/* Modal styling */
.modal-content {
border-radius: 8px;
background-color: #001f3f;
color: white;
}
/* Modal ticket list */
.modal-ticket-card {
border: 1px solid #444;
border-radius: 5px;
padding: 0.75rem 1rem;
margin-bottom: 0.75rem;
background: #002a5c;
}
.modal-ticket-ribbon {
height: 5px;
border-radius: 5px 5px 0 0;
}
.text-muted {
font-size: 0.9rem;
color: #bbb;
}
/* Form controls */
.form-control, .form-select {
background-color: #004080;
color: white;
border: 1px solid #0059b3;
}
.form-control::placeholder {
color: #aad4ff;
}
.form-control:focus, .form-select:focus {
background-color: #0059b3;
border-color: #3399ff;
color: white;
box-shadow: none;
}
/* Delete Customer button style */
.delete-customer-btn {
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
background-color: #dc3545; /* Bootstrap danger red */
color: white;
border: none;
border-radius: 0.25rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.delete-customer-btn:hover {
background-color: #b02a37; /* Darker red on hover */
}
/* Optional: add some padding to the table cells holding the button */
#customerTableBody td:last-child {
text-align: center;
vertical-align: middle;
padding: 0.3rem 0.5rem;
}
Greg’s Web Design KC
Ticket Management System
Customers
Select a customer
Select a customer
Open
Pending
Closed
<!– –>
// Data arrays
let customers = JSON.parse(localStorage.getItem(‘customers’)) || [
{ id: ‘cindy-edwards’, name: ‘Cindy Edwards’, email: ‘cindy@example.com’ }
];
let tickets = JSON.parse(localStorage.getItem(‘tickets’)) || [
{
id: ‘t1’,
subject: ‘Sample Ticket 1’,
customerId: ‘cindy-edwards’,
status: ‘Open’,
details: ‘Details about ticket 1…’,
createdDate: new Date().toLocaleString()
}
];
// Generate unique IDs for tickets/customers
function generateId(prefix = ‘id’) {
return prefix + ‘-‘ + Math.random().toString(36).substring(2, 9);
}
// Populate customer dropdown for new and edit ticket modals
function populateCustomerDropdown() {
const select = document.getElementById(‘ticketCustomer’);
select.innerHTML = ‘Select a customer’;
customers.forEach(c => {
const option = document.createElement(‘option’);
option.value = c.id;
option.textContent = c.name;
select.appendChild(option);
});
}
function populateEditCustomerDropdown() {
const select = document.getElementById(‘editTicketCustomer’);
select.innerHTML = ‘Select a customer’;
customers.forEach(c => {
const option = document.createElement(‘option’);
option.value = c.id;
option.textContent = c.name;
select.appendChild(option);
});
}
// Render tickets filtered by status open or pending (or all if no filter)
function renderTickets(filterStatus = [‘Open’, ‘Pending’]) {
const container = document.getElementById(‘ticketContainer’);
container.innerHTML = ”;
const filteredTickets = tickets.filter(t =>
filterStatus.includes(t.status)
);
if (filteredTickets.length === 0) {
container.innerHTML = ‘
No tickets matching the filter.
‘;
return;
}
filteredTickets.forEach(ticket => {
const card = document.createElement(‘div’);
card.className = ‘ticket-card’;
card.innerHTML = `
${ticket.subject}
Status: ${ticket.status}
${ticket.details.replace(/\n/g, ‘
‘)}
Created: ${ticket.createdDate}
`;
container.appendChild(card);
});
// Attach event listeners to Edit buttons
document.querySelectorAll(‘.edit-ticket-btn’).forEach(btn => {
btn.addEventListener(‘click’, e => {
const ticketId = e.target.getAttribute(‘data-ticket-id’);
openEditTicketModal(ticketId);
});
});
}
// Ribbon color class by status
function getRibbonClass(status) {
switch (status.toLowerCase()) {
case ‘open’: return ‘ribbon-open’;
case ‘pending’: return ‘ribbon-pending’;
case ‘closed’: return ‘ribbon-closed’;
default: return ”;
}
}
// Update status counts summary
function updateStatusCounts() {
const openCount = tickets.filter(t => t.status.toLowerCase() === ‘open’).length;
const pendingCount = tickets.filter(t => t.status.toLowerCase() === ‘pending’).length;
const closedCount = tickets.filter(t => t.status.toLowerCase() === ‘closed’).length;
document.getElementById(‘openCount’).textContent = openCount;
document.getElementById(‘pendingCount’).textContent = pendingCount;
document.getElementById(‘closedCount’).textContent = closedCount;
}
// Render customers table
function renderCustomerTable() {
const tbody = document.getElementById(‘customerTableBody’);
tbody.innerHTML = ”;
customers.forEach(cust => {
const tr = document.createElement(‘tr’);
tr.innerHTML = `
${cust.name} |
${cust.email} |
|
`;
tbody.appendChild(tr);
});
// Attach click listeners to customer names (show tickets)
document.querySelectorAll(‘.customer-name’).forEach(elem => {
elem.addEventListener(‘click’, e => {
const custId = e.target.getAttribute(‘data-customer-id’);
showCustomerTicketsModal(custId);
});
});
// Attach delete customer listeners
document.querySelectorAll(‘.delete-customer-btn’).forEach(btn => {
btn.addEventListener(‘click’, e => {
const custId = e.target.getAttribute(‘data-customer-id’);
if (confirm(‘Delete this customer and all their tickets?’)) {
deleteCustomer(custId);
}
});
});
}
// Show tickets modal for a customer
function showCustomerTicketsModal(customerId) {
const customer = customers.find(c => c.id === customerId);
if (!customer) return;
const modalTitle = document.getElementById(‘customerTicketsModalLabel’);
modalTitle.textContent = `Tickets for ${customer.name}`;
const container = document.getElementById(‘customerTicketsContainer’);
container.innerHTML = ”;
const custTickets = tickets.filter(t => t.customerId === customerId);
if (custTickets.length === 0) {
container.innerHTML = ‘
No tickets found for this customer.
‘;
} else {
custTickets.forEach(ticket => {
const div = document.createElement(‘div’);
div.className = ‘modal-ticket-card’;
div.innerHTML = `
${ticket.subject}
Status: ${ticket.status}
${ticket.details.replace(/\n/g, ‘
‘)}
Created: ${ticket.createdDate}
`;
container.appendChild(div);
});
}
// Show modal
const modalEl = document.getElementById(‘customerTicketsModal’);
const bsModal = new bootstrap.Modal(modalEl);
bsModal.show();
}
// Delete customer and their tickets
function deleteCustomer(customerId) {
customers = customers.filter(c => c.id !== customerId);
tickets = tickets.filter(t => t.customerId !== customerId);
localStorage.setItem(‘customers’, JSON.stringify(customers));
localStorage.setItem(‘tickets’, JSON.stringify(tickets));
updateStatusCounts();
renderTickets();
renderCustomerTable();
populateCustomerDropdown();
populateEditCustomerDropdown();
}
// Open Edit Ticket Modal and populate form
function openEditTicketModal(ticketId) {
const ticket = tickets.find(t => t.id === ticketId);
if (!ticket) return;
document.getElementById(‘editTicketId’).value = ticket.id;
document.getElementById(‘editTicketSubject’).value = ticket.subject;
document.getElementById(‘editTicketCustomer’).value = ticket.customerId;
document.getElementById(‘editTicketStatus’).value = ticket.status;
document.getElementById(‘editTicketDetails’).value = ticket.details;
const modalEl = document.getElementById(‘editTicketModal’);
const bsModal = new bootstrap.Modal(modalEl);
bsModal.show();
}
// Form handlers
// Add new ticket form
document.getElementById(‘newTicketForm’).addEventListener(‘submit’, e => {
e.preventDefault();
const subject = document.getElementById(‘ticketSubject’).value.trim();
const customerId = document.getElementById(‘ticketCustomer’).value;
const details = document.getElementById(‘ticketDetails’).value.trim();
if (!subject || !customerId || !details) {
alert(‘Please fill in all fields.’);
return;
}
const newTicket = {
id: generateId(‘t’),
subject,
customerId,
status: ‘Open’,
details,
createdDate: new Date().toLocaleString()
};
tickets.push(newTicket);
localStorage.setItem(‘tickets’, JSON.stringify(tickets));
updateStatusCounts();
renderTickets();
// Reset and close modal
e.target.reset();
const modalEl = document.getElementById(‘newTicketModal’);
const bsModal = bootstrap.Modal.getInstance(modalEl);
bsModal.hide();
});
// Add customer form
document.getElementById(‘addCustomerForm’).addEventListener(‘submit’, e => {
e.preventDefault();
const name = document.getElementById(‘customerName’).value.trim();
const email = document.getElementById(‘customerEmail’).value.trim();
if (!name || !email) {
alert(‘Please fill in all fields.’);
return;
}
// Check for duplicate emails
if (customers.some(c => c.email.toLowerCase() === email.toLowerCase())) {
alert(‘A customer with this email already exists.’);
return;
}
const newCustomer = {
id: generateId(‘c’),
name,
email
};
customers.push(newCustomer);
localStorage.setItem(‘customers’, JSON.stringify(customers));
renderCustomerTable();
populateCustomerDropdown();
populateEditCustomerDropdown();
// Reset and close modal
e.target.reset();
const modalEl = document.getElementById(‘addCustomerModal’);
const bsModal = bootstrap.Modal.getInstance(modalEl);
bsModal.hide();
});
// Edit ticket form submit
document.getElementById(‘editTicketForm’).addEventListener(‘submit’, e => {
e.preventDefault();
const id = document.getElementById(‘editTicketId’).value;
const subject = document.getElementById(‘editTicketSubject’).value.trim();
const customerId = document.getElementById(‘editTicketCustomer’).value;
const status = document.getElementById(‘editTicketStatus’).value;
const details = document.getElementById(‘editTicketDetails’).value.trim();
if (!subject || !customerId || !status || !details) {
alert(‘Please fill in all fields.’);
return;
}
const ticketIndex = tickets.findIndex(t => t.id === id);
if (ticketIndex === -1) return;
tickets[ticketIndex] = {
…tickets[ticketIndex],
subject,
customerId,
status,
details
};
localStorage.setItem(‘tickets’, JSON.stringify(tickets));
updateStatusCounts();
renderTickets();
const modalEl = document.getElementById(‘editTicketModal’);
const bsModal = bootstrap.Modal.getInstance(modalEl);
bsModal.hide();
});
// Filter tickets when clicking status boxes
document.getElementById(‘openTicketsBox’).addEventListener(‘click’, () => renderTickets([‘Open’]));
document.getElementById(‘pendingTicketsBox’).addEventListener(‘click’, () => renderTickets([‘Pending’]));
document.getElementById(‘closedTicketsBox’).addEventListener(‘click’, () => renderTickets([‘Closed’]));
// On page load
populateCustomerDropdown();
populateEditCustomerDropdown();
updateStatusCounts();
renderTickets();
renderCustomerTable();