Dateien nach "public" hochladen

This commit is contained in:
Honey committed 2025-11-15 13:48:37 +01:00
1 parent 9df0922824
commit 58ceca14b3
5 files changed
+678

No files matched your search

+150
View File
@@ -0,0 +1,150 @@
document.addEventListener('DOMContentLoaded', () => {
const ticketsBody = document.getElementById('tickets-body');
const closedTicketsBody = document.getElementById('closed-tickets-body');
const searchInput = document.getElementById('search-tickets');
const statusFilter = document.getElementById('status-filter');
const priorityFilter = document.getElementById('priority-filter');
const categoryFilter = document.getElementById('category-filter');
const statTotal = document.getElementById('total-tickets');
const statOpen = document.getElementById('open-tickets');
const statInProgress = document.getElementById('in-progress-tickets');
const statClosed = document.getElementById('closed-tickets');
const statTotalTime = document.getElementById('time-tickets'); // Neu: Gesamtzeit Anzeige
searchInput.addEventListener('input', refreshTickets);
statusFilter.addEventListener('change', refreshTickets);
priorityFilter.addEventListener('change', refreshTickets);
categoryFilter.addEventListener('change', refreshTickets);
// Helper: Status/Priority zu "Title-Case-With-Dash"
function toTitleCaseWithDash(str) {
return str
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('-');
}
async function fetchTickets() {
try {
const res = await fetch('/api/tickets');
return await res.json();
} catch (err) {
console.error('Error fetching tickets:', err);
return [];
}
}
async function deleteTicket(id) {
try {
const res = await fetch(`/api/tickets/${id}`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ actor: 'Admin' })
});
return await res.json();
} catch (err) {
console.error('Error deleting ticket:', err);
}
}
async function refreshTickets() {
const tickets = await fetchTickets();
renderTickets(tickets);
updateStats(tickets);
}
function renderTickets(tickets) {
const status = statusFilter.value;
const priority = priorityFilter.value;
const category = categoryFilter.value;
const searchTerm = searchInput.value.toLowerCase();
const filtered = tickets.filter(t =>
(status === 'all' || t.status === status) &&
(priority === 'all' || t.priority === priority) &&
(category === 'all' || t.category === category) &&
(t.title.toLowerCase().includes(searchTerm) ||
t.description.toLowerCase().includes(searchTerm) ||
t.requester.toLowerCase().includes(searchTerm))
).sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
const openRows = [];
const closedRows = [];
filtered.forEach(ticket => {
const shortTitle = ticket.title.length > 50 ? ticket.title.slice(0, 50) + '...' : ticket.title;
// Wandle Status und Priority korrekt in Klassen um
const statusClass = toTitleCaseWithDash(ticket.status || 'Unknown');
const priorityClass = toTitleCaseWithDash(ticket.priority || 'None');
const rowHTML = `
<tr>
<td>${ticket.id}</td>
<td class="ticket-title">${shortTitle}</td>
<td>${ticket.requester}</td>
<td>${ticket.category || '-'}</td>
<td><span class="status-badge status-${statusClass}">${ticket.status || 'Unknown'}</span></td>
<td><span class="priority-badge priority-${priorityClass}">${ticket.priority || 'None'}</span></td>
<td>${formatDate(ticket.created_at)}</td>
<td>${ticket.total_time} min</td>
<td class="table-actions">
<button class="table-btn view-btn" data-id="${ticket.id}"><i class="fas fa-eye"></i></button>
<button class="table-btn delete-btn" data-id="${ticket.id}"><i class="fas fa-trash-alt"></i></button>
</td>
</tr>`;
if (ticket.status === 'Closed') {
closedRows.push(rowHTML);
} else {
openRows.push(rowHTML);
}
});
ticketsBody.innerHTML = openRows.length
? openRows.join('')
: `<tr><td colspan="8" class="empty-table">No open tickets found</td></tr>`;
if (closedTicketsBody) {
closedTicketsBody.innerHTML = closedRows.length
? closedRows.join('')
: `<tr><td colspan="8" class="empty-table">No closed tickets</td></tr>`;
}
// Event listeners an Buttons hängen
[...document.querySelectorAll('.view-btn')].forEach(btn =>
btn.addEventListener('click', () => {
window.location.href = `/ticketview/ticket-view-admin.html?id=${btn.dataset.id}`;
})
);
[...document.querySelectorAll('.delete-btn')].forEach(btn =>
btn.addEventListener('click', () => {
if (confirm('Are you sure you want to delete this ticket?')) {
deleteTicket(btn.dataset.id).then(refreshTickets);
}
})
);
}
function updateStats(tickets) {
statTotal.textContent = tickets.length;
statOpen.textContent = tickets.filter(t => t.status === 'Open').length;
statInProgress.textContent = tickets.filter(t => t.status === 'In Progress').length;
statClosed.textContent = tickets.filter(t => t.status === 'Closed').length;
// Gesamtzeit aller Tickets summieren (total_time in Minuten)
const totalTime = tickets.reduce((sum, ticket) => sum + (ticket.total_time || 0), 0);
statTotalTime.textContent = `${totalTime} min`;
}
function formatDate(date) {
return new Date(date).toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}
refreshTickets();
});
+256
View File
@@ -0,0 +1,256 @@
/* admin.css - Dark Mode Only */
/* ===== GLOBAL STYLES & VARIABLES ===== */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
:root {
--primary-color: #4A90E2;
--primary-hover-color: #357ABD;
--secondary-color: #50E3C2;
--accent-color: #F5A623;
--success-color: #7ED321;
--danger-color: #D0021B;
--neutral-dark: #f0f0f0;
--neutral-medium: #cccccc;
--neutral-light: #999999;
--bg-color: #121212;
--card-bg-color: #1e1e1e;
--border-color: #2a2a2a;
--shadow-color: rgba(0, 0, 0, 0.5);
--text-color: #f5f5f5;
--link-color: var(--primary-color);
--font-family: 'Inter', sans-serif;
--border-radius: 8px;
--card-padding: 25px;
--input-padding: 12px 15px;
--transition-speed: 0.2s ease-in-out;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family);
background-color: var(--bg-color);
color: var(--text-color);
font-size: 16px;
line-height: 1.6;
}
a {
color: var(--link-color);
text-decoration: none;
transition: color var(--transition-speed);
}
a:hover {
color: var(--primary-hover-color);
text-decoration: underline;
}
/* ===== UTILITY CLASSES ===== */
.container {
max-width: 1400px;
margin: auto;
padding: 20px 30px;
}
/* ===== HEADER ===== */
.header {
background: var(--card-bg-color);
padding: 20px var(--card-padding);
border-radius: var(--border-radius);
box-shadow: 0 2px 10px var(--shadow-color);
margin-bottom: 30px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
}
.header h1 {
color: var(--primary-color);
font-size: 1.7rem;
font-weight: 700;
display: flex;
align-items: center;
}
.header h1 i {
margin-right: 12px;
}
/* ===== SECTION HEADER ===== */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
flex-wrap: wrap;
gap: 15px;
}
.section-header h2 {
color: var(--neutral-dark);
font-size: 1.6rem;
font-weight: 700;
}
.section-header h2 i {
margin-right: 10px;
color: var(--primary-color);
}
/* ===== STATS CARDS ===== */
.admin-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
background: var(--card-bg-color);
padding: var(--card-padding);
border-radius: var(--border-radius);
margin-bottom: 30px;
box-shadow: 0 4px 15px var(--shadow-color);
}
.stat-card {
text-align: center;
}
.stat-value {
font-size: 2.5rem;
color: var(--primary-color);
font-weight: bold;
}
.stat-label {
font-size: 1rem;
color: var(--neutral-medium);
}
/* ===== FILTERS ===== */
.admin-filters {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.admin-filters input,
.admin-filters select {
padding: var(--input-padding);
background: #1c1c1c;
border: 1px solid var(--border-color);
color: var(--text-color);
border-radius: var(--border-radius);
font-size: 0.95rem;
}
/* ===== TICKET TABLE STYLES (OPEN + CLOSED) ===== */
.admin-tickets-list {
background: var(--card-bg-color);
padding: var(--card-padding);
border-radius: var(--border-radius);
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.95rem;
}
table th,
table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--border-color);
color: var(--text-color);
}
table th {
background: #1a1a1a;
color: var(--neutral-medium);
font-weight: 600;
text-transform: uppercase;
font-size: 0.8rem;
}
/* ===== TABLE ACTION BUTTONS ===== */
.table-actions {
display: flex;
gap: 8px;
justify-content: center;
}
.table-btn {
padding: 6px 10px;
border-radius: var(--border-radius);
color: white;
font-size: 0.85rem;
display: flex;
align-items: center;
gap: 5px;
cursor: pointer;
}
.view-btn {
background-color: var(--primary-color);
}
.view-btn:hover {
background-color: var(--primary-hover-color);
}
.delete-btn {
background-color: var(--danger-color);
}
.delete-btn:hover {
background-color: #a60014;
}
/* ===== BADGES ===== */
.status-Open { background-color: rgba(74, 144, 226, 0.15); color: #4A90E2; }
.status-In-Progress { background-color: rgba(245, 166, 35, 0.15); color: #F5A623; }
.status-On-Hold { background-color: rgba(155, 155, 155, 0.15); color: #9B9B9B; }
.status-Closed { background-color: rgba(126, 211, 33, 0.15); color: #7ED321; }
.priority-Low { background-color: rgba(80, 227, 194, 0.2); color: #50E3C2; }
.priority-Medium { background-color: rgba(74, 144, 226, 0.2); color: #4A90E2; }
.priority-High { background-color: rgba(245, 166, 35, 0.2); color: #F5A623; }
.priority-Urgent { background-color: rgba(208, 2, 27, 0.15); color: #D0021B; }
.status-badge,
.priority-badge {
padding: 0.25em 0.6em;
border-radius: 9999px;
}
/* ===== CLOSED TICKETS SECTION ===== */
.closed-tickets-section {
margin-top: 30px;
}
.closed-tickets-section .section-header h2 {
color: var(--success-color);
}
/* ===== RESPONSIVE ===== */
@media (max-width: 768px) {
.container {
padding: 15px 20px;
}
.header {
flex-direction: column;
align-items: flex-start;
}
.section-header {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
}
@media (max-width: 480px) {
.header h1 {
font-size: 1.3rem;
}
.stat-value {
font-size: 2rem;
}
.btn-primary,
.btn-small {
width: 100%;
}
table {
font-size: 0.85rem;
}
table th,
table td {
padding: 8px 10px;
}
}
+127
View File
@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard - Ticket System</title>
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico">
<link rel="stylesheet" href="user.css" />
<link rel="stylesheet" href="admin.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div class="container">
<header class="header">
<h1><i class="fas fa-ticket-alt"></i> Admin Dashboard</h1>
<div>
<span>Welcome, Admin!</span>
<a href="/">User View</a>
</div>
</header>
<main class="main-content">
<section class="admin-stats">
<div class="stat-card">
<div class="stat-value" id="total-tickets">0</div>
<div class="stat-label">Total Tickets</div>
</div>
<div class="stat-card">
<div class="stat-value" id="open-tickets">0</div>
<div class="stat-label">Open Tickets</div>
</div>
<div class="stat-card">
<div class="stat-value" id="in-progress-tickets">0</div>
<div class="stat-label">In Progress</div>
</div>
<div class="stat-card">
<div class="stat-value" id="closed-tickets">0</div>
<div class="stat-label">Closed Tickets</div>
</div>
<div class="stat-card">
<div class="stat-value" id="time-tickets">0</div>
<div class="stat-label">Total Time</div>
</div>
</section>
<section class="tickets-section">
<div class="section-header">
<h2><i class="fas fa-tasks"></i> All Tickets</h2>
<div class="admin-filters">
<input type="text" id="search-tickets" placeholder="Search tickets...">
<select id="status-filter">
<option value="all">All Statuses</option>
<option value="Open">Open</option>
<option value="In Progress">In Progress</option>
<option value="On Hold">On Hold</option>
<option value="Closed">Closed</option>
</select>
<select id="priority-filter">
<option value="all">All Priorities</option>
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
<option value="Urgent">Urgent</option>
</select>
<select id="category-filter">
<option value="all">All Categories</option>
<option value="Bug">Bug</option>
<option value="Feature">Feature</option>
<option value="Support">Support</option>
<option value="Other">Other</option>
</select>
</div>
</div>
<div class="admin-tickets-list">
<table id="tickets-table">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Requester</th>
<th>Category</th>
<th>Status</th>
<th>Priority</th>
<th>Created</th>
<th>Total Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="tickets-body"></tbody>
</table>
</div>
</section>
</main>
<section class="closed-tickets-section">
<div class="section-header">
<h2><i class="fas fa-archive"></i> Closed Tickets</h2>
</div>
<div class="admin-tickets-list">
<table id="closed-tickets-table">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Requester</th>
<th>Category</th>
<th>Status</th>
<th>Priority</th>
<th>Created</th>
<th>Total Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="closed-tickets-body"></tbody>
</table>
</div>
</section>
<footer class="footer">
<p>Ticketsystem by Honey v1.1 &copy; 2025</p>
</footer>
</div>
<script src="admin-script.js"></script>
</body>
</html>
+142
View File
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Laila Dashboard - Ticket System!</title>
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico">
<link rel="stylesheet" href="user.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
/>
</head>
<body>
<div class="container">
<header class="header">
<h1><i class="fas fa-ticket-alt"></i> My Tickets</h1>
<div class="user-actions">
<span>Hi Laila!</span>
<img class="avatar" src="/assets/user.jpg" alt="User Avatar">
<button id="open-modal-btn" class="btn-primary small-btn">
<i class="fas fa-plus"></i> Create Ticket
</button>
<button id="update-pfp-btn" class="btn-update-pfp small-btn">
<i class="fas fa-camera"></i> Update Picture
</button>
<a href="/admin.html" class="admin-link">Admin Dashboard</a>
</div>
</header>
<!-- Modal Window for Creating Ticket -->
<div id="create-ticket-modal" class="modal" style="display:none;">
<div class="modal-content">
<button id="close-modal-btn" class="modal-close-btn" aria-label="Close modal">
&times;
</button>
<section class="ticket-form">
<h2><i class="fas fa-plus-circle"></i> Create New Ticket</h2>
<form id="create-form">
<div class="form-group">
<input type="text" id="title" placeholder="Ticket Title" required />
</div>
<div class="form-group">
<textarea
id="description"
rows="3"
placeholder="Describe your issue..."
required
></textarea>
</div>
<div class="form-row">
<div class="form-group">
<select id="priority">
<option value="Low">Low Priority</option>
<option value="Medium" selected>Medium Priority</option>
<option value="High">High Priority</option>
<option value="Urgent">Urgent</option>
</select>
</div>
<div class="form-group">
<select id="category">
<option value="HELP">HELP</option>
<option value="HELP MEE NOOOW">HELP MEE NOOOW</option>
<option value="IT-Support">IT-Support</option>
<option value="Suggestion...">Suggestion...</option>
<option value="Add Book pls">Add Book pls</option>
<option value="Honeyycloud!">Honeyycloud!</option>
</select>
</div>
</div>
<div class="form-group button-wrapper">
<button type="submit" class="btn-primary">
<i class="fas fa-paper-plane"></i> Create Ticket
</button>
</div>
</form>
</section>
</div>
</div>
<!-- Profile Picture Modal -->
<div id="update-pfp-modal" class="modal" style="display:none;">
<div class="modal-content small-modal">
<h2><i class="fas fa-camera"></i> Update Profile Picture</h2>
<div id="drop-zone">
<p>Drag & Drop your picture here<br>or click to select</p>
<input type="file" id="pfp-file-input" accept="image/*" hidden>
</div>
<button id="upload-pfp-btn" class="btn-update-pfp" style="margin-top: 12px;">
<i class="fas fa-upload"></i> Upload
</button>
</div>
</div>
<main class="main-content">
<section class="tickets-section">
<div class="section-header">
<h2><i class="fas fa-tasks"></i> My Tickets</h2>
<div class="filters-row">
<input type="text" id="search-input" placeholder="Search tickets..." />
<select id="status-filter">
<option value="all">All Statuses</option>
<option value="Open">Open</option>
<option value="In Progress">In Progress</option>
<option value="On Hold">On Hold</option>
</select>
<select id="category-filter">
<option value="all">All Categories</option>
<option value="HELP">HELP</option>
<option value="HELP MEE NOOOW">HELP MEE NOOOW</option>
<option value="IT-Support">IT-Support</option>
<option value="Suggestion...">Suggestion...</option>
<option value="Add Book pls">Add Book pls</option>
<option value="Honeyycloud!">Honeyycloud!</option>
</select>
</div>
</div>
<!-- 🟢 Open/Active Tickets -->
<div>
<h3>Active Tickets</h3>
<div id="tickets-container" class="tickets-grid"></div>
</div>
<!-- 🔴 Closed Tickets -->
<div style="margin-top: 2rem;">
<h3>Closed Tickets</h3>
<div id="closed-tickets-container" class="tickets-grid"></div>
</div>
</section>
</main>
<footer class="footer">
<p>Ticket System by Honey v1.1 &copy; 2025</p>
</footer>
</div>
<script src="user-script.js"></script>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
minify the css and javascript maybe
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned