termine ticketboard update auf ajax
This commit is contained in:
1 parent
66eb55d264
commit
854e6d03cb
3 files changed
+248
-84
No files matched your search
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../../../controller/RoutesController.php');
|
||||
include_once(__DIR__ . '/../../../modules/Mail.php');
|
||||
|
||||
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
|
||||
$action = $daten['action'] ?? '';
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
// ---------- Liste laden (nur eigene Tickets) ----------
|
||||
case 'get_entries': {
|
||||
$entries = DatabaseShow(
|
||||
"Tickets",
|
||||
"ID, TITEL, BESCHREIBUNG, STATUS",
|
||||
"ERSTELLER = ?",
|
||||
((new getSpecificData())->getUsername()),
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true, 'entries' => array_values($entries ?? [])]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Neues Ticket anlegen ----------
|
||||
case 'add_entry': {
|
||||
foreach (['titel', 'beschreibung'] as $feld) {
|
||||
if (empty($daten[$feld])) {
|
||||
throw new InvalidArgumentException("Pflichtfeld fehlt: $feld");
|
||||
}
|
||||
}
|
||||
|
||||
$titel = $daten['titel'];
|
||||
$beschreibung = $daten['beschreibung'];
|
||||
$user = ucfirst(strtolower((new getSpecificData())->getUsername()));
|
||||
|
||||
DatabaseInsert(
|
||||
"Tickets",
|
||||
"TITEL, BESCHREIBUNG, STATUS, ERSTELLER",
|
||||
$titel,
|
||||
$beschreibung,
|
||||
"OFFEN",
|
||||
$user,
|
||||
);
|
||||
|
||||
sendMail(
|
||||
"Neues Ticket von $user",
|
||||
"Titel: $titel\nBeschreibung: $beschreibung",
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
// ---------- Ticket löschen ----------
|
||||
case 'delete_entry': {
|
||||
$id = $daten['id'] ?? null;
|
||||
|
||||
if (!is_numeric($id)) {
|
||||
throw new InvalidArgumentException('Ungültige ID');
|
||||
}
|
||||
|
||||
DatabaseDelete("Tickets", "ID = ?", (int)$id);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidArgumentException("Unbekannte Aktion: $action");
|
||||
}
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
error_log($e->getMessage());
|
||||
echo json_encode(['error' => 'Serverfehler']);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
const TICKETS_HANDLER_URL = '/api/js/handler/TicketBoardAjaxHandler';
|
||||
|
||||
async function ticketsAjaxRequest(payload) {
|
||||
try {
|
||||
const response = await fetch(TICKETS_HANDLER_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
const data = await parseJsonResponse(response);
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
return data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Tickets-Ajax-Fehler:', error.message);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function getTicketEntries() {
|
||||
return ticketsAjaxRequest({ action: 'get_entries' });
|
||||
}
|
||||
|
||||
function addTicketEntry(titel, beschreibung) {
|
||||
return ticketsAjaxRequest({ action: 'add_entry', titel, beschreibung });
|
||||
}
|
||||
|
||||
function deleteTicketEntry(id) {
|
||||
return ticketsAjaxRequest({ action: 'delete_entry', id });
|
||||
}
|
||||
@@ -1,101 +1,144 @@
|
||||
<?php
|
||||
include_once(__DIR__ . '/../controller/DatabaseController.php');
|
||||
include_once(__DIR__ . '/../controller/RoutesController.php');
|
||||
include_once(__DIR__ . '/../modules/Mail.php');
|
||||
|
||||
$view = $_GET['view'] ?? 'show';
|
||||
$selectedService = $_GET['service'] ?? null;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_entry'])) {
|
||||
$titel = $_POST['titel'];
|
||||
$beschreibung = $_POST['beschreibung'];
|
||||
|
||||
DatabaseInsert(
|
||||
"Tickets",
|
||||
"TITEL, BESCHREIBUNG, STATUS, ERSTELLER",
|
||||
$titel,
|
||||
$beschreibung,
|
||||
"OFFEN",
|
||||
ucfirst(strtolower((new getSpecificData())->getUsername())),
|
||||
);
|
||||
|
||||
$html = "Titel: $titel\nBeschreibung: $beschreibung";
|
||||
$user = ucfirst(strtolower((new getSpecificData())->getUsername()));
|
||||
|
||||
sendMail(
|
||||
"Neues Ticket von $user",
|
||||
$html,
|
||||
(new getSpecificData())->getRequestURL(),
|
||||
true,
|
||||
);
|
||||
|
||||
redirectToWebsite((new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_entry'])) {
|
||||
$id = $_POST['id'];
|
||||
|
||||
DatabaseDelete("Tickets", "ID = ?", $id);
|
||||
|
||||
redirectToWebsite((new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
$entries = DatabaseShow(
|
||||
"Tickets",
|
||||
"TITEL, BESCHREIBUNG, STATUS",
|
||||
"ERSTELLER = ?",
|
||||
((new getSpecificData())->getUsername()),
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
<div>
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
<div class="vergabe-form">
|
||||
<h3>Neues Ticket anlegen</h3>
|
||||
<form method="POST" class="vergabe-grid">
|
||||
<form id="addTicketForm" class="vergabe-grid">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Titel</label>
|
||||
<input type="text" name="titel" required>
|
||||
<input type="text" name="titel" id="in-titel" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Beschreibung</label>
|
||||
<input type="text" name="beschreibung" required>
|
||||
<input type="text" name="beschreibung" id="in-beschreibung" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="add_entry" class="btn-submit-vergabe">
|
||||
💾 Speichern
|
||||
</button>
|
||||
<button type="submit" class="btn-submit-vergabe">💾 Speichern</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>Tickets</h3>
|
||||
|
||||
<?php if (!empty($entries)): ?>
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titel</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($entries as $entry): ?>
|
||||
<tr>
|
||||
<td data-label="Titel"><?= htmlspecialchars($entry['TITEL'] ?? '') ?></td>
|
||||
<td data-label="Beschreibung"><?= htmlspecialchars($entry['BESCHREIBUNG'] ?? '') ?></td>
|
||||
<td data-label="Status"><?= htmlspecialchars($entry['STATUS'] ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php else: ?>
|
||||
<p>Keine Tickets vorhanden.</p>
|
||||
<?php endif; ?>
|
||||
<div id="tableWrapper">
|
||||
<p>Lade Daten...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/AjaxMethods.js"></script>
|
||||
<script src="/js/ajaxApi/TicketBoardAjaxMethods.js"></script>
|
||||
<script src="/js/AjaxRenderTables.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const tableWrapper = document.getElementById('tableWrapper');
|
||||
const POLL_INTERVAL_MS = 5000;
|
||||
let lastStatusById = {};
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Tabelle rendern (eigene Render-Funktion wg. Status-Highlight)
|
||||
// -----------------------------------------------------------
|
||||
function renderTicketsTabelle(entries) {
|
||||
if (!entries || entries.length === 0) {
|
||||
tableWrapper.innerHTML = '<p>Keine Tickets vorhanden.</p>';
|
||||
lastStatusById = {};
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titel</th><th>Beschreibung</th><th>Status</th>
|
||||
<th style="width: 100px;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
for (const e of entries) {
|
||||
const changed = lastStatusById[e.ID] !== undefined &&
|
||||
lastStatusById[e.ID] !== e.STATUS;
|
||||
|
||||
html += `
|
||||
<tr data-id="${escapeHtml(e.ID)}" class="${changed ? 'status-row-changed' : ''}">
|
||||
<td data-label="Titel">${escapeHtml(e.TITEL)}</td>
|
||||
<td data-label="Beschreibung">${escapeHtml(e.BESCHREIBUNG)}</td>
|
||||
<td data-label="Status"><span class="status-badge">${escapeHtml(e.STATUS)}</span></td>
|
||||
<td data-label="Aktion">
|
||||
<button type="button" class="btn-delete" data-id="${escapeHtml(e.ID)}">Löschen</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
html += `</tbody></table>`;
|
||||
tableWrapper.innerHTML = html;
|
||||
|
||||
const neueStatus = {};
|
||||
entries.forEach(e => {
|
||||
neueStatus[e.ID] = e.STATUS;
|
||||
});
|
||||
lastStatusById = neueStatus;
|
||||
|
||||
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
|
||||
btn.addEventListener('click', async () => {
|
||||
if (!confirm('Möchtest du dieses Ticket wirklich löschen?')) return;
|
||||
try {
|
||||
await deleteTicketEntry(btn.dataset.id);
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Löschen: ' + err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function ladeListe() {
|
||||
try {
|
||||
const data = await getTicketEntries();
|
||||
renderTicketsTabelle(data.entries);
|
||||
} catch (err) {
|
||||
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Ticket anlegen
|
||||
// -----------------------------------------------------------
|
||||
document.getElementById('addTicketForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const titel = document.getElementById('in-titel').value.trim();
|
||||
const beschreibung = document.getElementById('in-beschreibung').value.trim();
|
||||
|
||||
if (!titel || !beschreibung) return;
|
||||
|
||||
try {
|
||||
await addTicketEntry(titel, beschreibung);
|
||||
e.target.reset();
|
||||
await ladeListe();
|
||||
} catch (err) {
|
||||
alert('Fehler beim Speichern: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Initialisierung + zyklische Aktualisierung
|
||||
// -----------------------------------------------------------
|
||||
ladeListe();
|
||||
setInterval(ladeListe, POLL_INTERVAL_MS);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@keyframes statusChanged {
|
||||
0% {
|
||||
background-color: #fff3cd;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.status-row-changed {
|
||||
animation: statusChanged 2.5s ease-out;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user