webtime service fixes
This commit is contained in:
1 parent
9ff26f0f59
commit
6336442f2e
7 files changed
+476
-50
No files matched your search
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Model\Zeiterfassung;
|
||||
use MVC\Model\Users;
|
||||
|
||||
class WebtimeController extends Controller {
|
||||
|
||||
public function webtime() {
|
||||
|
||||
// User aus Session + DB laden
|
||||
$username = $_SESSION['username'] ?? null;
|
||||
|
||||
if (!$username) {
|
||||
header('Location: ?service=login');
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = Users::getByUsername($username);
|
||||
$userId = (int)($user['ID'] ?? 0);
|
||||
|
||||
if (!$userId) {
|
||||
header('Location: ?service=login');
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Einstempeln ---
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'start') {
|
||||
if (!Zeiterfassung::isRunning($userId)) {
|
||||
Zeiterfassung::insert([
|
||||
'DATUM' => date('d.m.Y'),
|
||||
'BEGIN' => date('H:i'),
|
||||
'USER_ID' => $userId,
|
||||
]);
|
||||
}
|
||||
header('Location: ?service=webtime');
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Ausstempeln + Aufgaben speichern ---
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'stop') {
|
||||
$open = Zeiterfassung::getOpenEntry($userId);
|
||||
if ($open) {
|
||||
Zeiterfassung::update(
|
||||
[
|
||||
'ENDE' => date('H:i'),
|
||||
'A_1' => $_POST['a_1'] ?? null,
|
||||
'A_2' => $_POST['a_2'] ?? null,
|
||||
'A_3' => $_POST['a_3'] ?? null,
|
||||
'A_4' => $_POST['a_4'] ?? null,
|
||||
],
|
||||
"ID = ?",
|
||||
$open['ID']
|
||||
);
|
||||
}
|
||||
header('Location: ?service=webtime');
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Eintrag bearbeiten ---
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'edit') {
|
||||
$id = (int)($_POST['entry_id'] ?? 0);
|
||||
if ($id) {
|
||||
Zeiterfassung::update(
|
||||
[
|
||||
'DATUM' => $_POST['datum'],
|
||||
'BEGIN' => $_POST['begin'],
|
||||
'ENDE' => $_POST['ende'] !== '' ? $_POST['ende'] : null,
|
||||
'A_1' => $_POST['a_1'] ?? null,
|
||||
'A_2' => $_POST['a_2'] ?? null,
|
||||
'A_3' => $_POST['a_3'] ?? null,
|
||||
'A_4' => $_POST['a_4'] ?? null,
|
||||
],
|
||||
"ID = ? AND USER_ID = ?",
|
||||
$id, $userId
|
||||
);
|
||||
}
|
||||
header('Location: ?service=webtime');
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Eintrag löschen ---
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delete') {
|
||||
$id = (int)($_POST['entry_id'] ?? 0);
|
||||
if ($id) {
|
||||
Zeiterfassung::delete("ID = ? AND USER_ID = ?", $id, $userId);
|
||||
}
|
||||
header('Location: ?service=webtime');
|
||||
exit;
|
||||
}
|
||||
|
||||
$isRunning = Zeiterfassung::isRunning($userId);
|
||||
$openEntry = Zeiterfassung::getOpenEntry($userId);
|
||||
$eintraege = Zeiterfassung::getByUser($userId);
|
||||
|
||||
return $this->render('webtime', [
|
||||
'isRunning' => $isRunning,
|
||||
'openEntry' => $openEntry,
|
||||
'eintraege' => $eintraege,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MVC\Controller;
|
||||
|
||||
use MVC\Core\Controller;
|
||||
use MVC\Classes\Client;
|
||||
use MVC\Classes\Cookie;
|
||||
use MVC\Classes\Request;
|
||||
use MVC\Classes\Server;
|
||||
// use MVC\Model\YourModel; // Importiere dein Model
|
||||
|
||||
class ZeiterfassungController extends Controller {
|
||||
public function zeiterfassung() {
|
||||
// Nutze Models für den Datenbankzugriff
|
||||
// $data = YourModel::all();
|
||||
|
||||
return $this->render('zeiterfassung', [
|
||||
'title' => 'Zeiterfassung Modul',
|
||||
'message' => 'Willkommen in deinem neuen Modul Zeiterfassung!'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -4,19 +4,26 @@ namespace MVC\Model;
|
||||
|
||||
use MVC\Core\Model;
|
||||
|
||||
/**
|
||||
* Model für die Tabelle 'Zeiterfassung'
|
||||
* Verfügbare Spalten: 'ID', ''
|
||||
*/
|
||||
class Zeiterfassung extends Model {
|
||||
protected static $table = "Zeiterfassung";
|
||||
|
||||
// Hier kannst du spezifische Methoden für dieses Model hinzufügen
|
||||
|
||||
/**
|
||||
* Beispiel: Suche nach einer spezifischen Spalte
|
||||
*/
|
||||
public static function getByFirstColumn($value) {
|
||||
return self::where("ID = ?", $value);
|
||||
public static function getByUser(int $userId): array {
|
||||
return self::where("USER_ID = ? ORDER BY ID DESC", $userId);
|
||||
}
|
||||
}
|
||||
|
||||
public static function isRunning(int $userId): bool {
|
||||
$result = self::where(
|
||||
"USER_ID = ? AND ENDE IS NULL LIMIT 1",
|
||||
$userId
|
||||
);
|
||||
return !empty($result);
|
||||
}
|
||||
|
||||
public static function getOpenEntry(int $userId): ?array {
|
||||
$result = self::where(
|
||||
"USER_ID = ? AND ENDE IS NULL ORDER BY ID DESC LIMIT 1",
|
||||
$userId
|
||||
);
|
||||
return $result[0] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
<h2>⏱️ WebTime – Zeiterfassung</h2>
|
||||
|
||||
<!-- STATUS-KARTE -->
|
||||
<div class="status-card <?= $isRunning ? 'running' : 'stopped' ?>">
|
||||
<?php if ($isRunning && $openEntry): ?>
|
||||
<div class="status-info">
|
||||
<span class="status-dot"></span>
|
||||
<strong>Läuft seit <?= htmlspecialchars($openEntry['BEGIN']) ?> Uhr</strong>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="stop">
|
||||
<div class="aufgaben-grid">
|
||||
<input type="text" name="a_1"
|
||||
value="<?= htmlspecialchars($openEntry['A_1'] ?? '') ?>"
|
||||
placeholder="Aufgabe 1" class="aufgabe-input">
|
||||
<input type="text" name="a_2"
|
||||
value="<?= htmlspecialchars($openEntry['A_2'] ?? '') ?>"
|
||||
placeholder="Aufgabe 2" class="aufgabe-input">
|
||||
<input type="text" name="a_3"
|
||||
value="<?= htmlspecialchars($openEntry['A_3'] ?? '') ?>"
|
||||
placeholder="Aufgabe 3" class="aufgabe-input">
|
||||
<input type="text" name="a_4"
|
||||
value="<?= htmlspecialchars($openEntry['A_4'] ?? '') ?>"
|
||||
placeholder="Aufgabe 4" class="aufgabe-input">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-stop">⏹ Ausstempeln</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div class="status-info">
|
||||
<span class="status-dot"></span>
|
||||
<strong>Nicht eingestempelt</strong>
|
||||
</div>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="start">
|
||||
<button type="submit" class="btn btn-start">▶ Einstempeln</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- TABELLE -->
|
||||
<div class="card" style="margin-top: 30px;">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<th>Beginn</th>
|
||||
<th>Ende</th>
|
||||
<th>Dauer</th>
|
||||
<th>Aufgaben</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($eintraege as $e):
|
||||
$dauer = '–';
|
||||
if (!empty($e['BEGIN']) && !empty($e['ENDE'])) {
|
||||
$diff = strtotime($e['ENDE']) - strtotime($e['BEGIN']);
|
||||
if ($diff > 0) {
|
||||
$dauer = sprintf('%02d:%02d h', floor($diff / 3600), floor(($diff % 3600) / 60));
|
||||
}
|
||||
}
|
||||
$aufgaben = array_filter([
|
||||
$e['A_1'] ?? null,
|
||||
$e['A_2'] ?? null,
|
||||
$e['A_3'] ?? null,
|
||||
$e['A_4'] ?? null,
|
||||
]);
|
||||
?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($e['DATUM'] ?? '') ?></td>
|
||||
<td><?= htmlspecialchars($e['BEGIN'] ?? '') ?></td>
|
||||
<td>
|
||||
<?php if (!empty($e['ENDE'])): ?>
|
||||
<?= htmlspecialchars($e['ENDE']) ?>
|
||||
<?php else: ?>
|
||||
<span class="badge-running">läuft</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= $dauer ?></td>
|
||||
<td>
|
||||
<?php foreach ($aufgaben as $a): ?>
|
||||
<span class="tag"><?= htmlspecialchars($a) ?></span>
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-icon"
|
||||
onclick='openEdit(<?= htmlspecialchars(json_encode($e), ENT_QUOTES) ?>)'>✏️</button>
|
||||
<form method="POST" style="display:inline"
|
||||
onsubmit="return confirm('Eintrag löschen?')">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="entry_id" value="<?= (int)$e['ID'] ?>">
|
||||
<button type="submit" class="btn-icon">🗑️</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($eintraege)): ?>
|
||||
<tr>
|
||||
<td colspan="6" style="text-align:center; padding: 20px; opacity: 0.5;">
|
||||
Noch keine Einträge vorhanden.
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- EDIT MODAL -->
|
||||
<div id="editModal" class="modal" style="display:none">
|
||||
<div class="modal-box">
|
||||
<h3 style="margin-top:0">✏️ Eintrag bearbeiten</h3>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="edit">
|
||||
<input type="hidden" name="entry_id" id="edit_id">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Datum</label>
|
||||
<input type="text" name="datum" id="edit_datum"
|
||||
class="form-control" placeholder="01.01.2025">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Beginn</label>
|
||||
<input type="text" name="begin" id="edit_begin"
|
||||
class="form-control" placeholder="08:00">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ende</label>
|
||||
<input type="text" name="ende" id="edit_ende"
|
||||
class="form-control" placeholder="17:00">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Aufgabe 1</label>
|
||||
<input type="text" name="a_1" id="edit_a_1" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Aufgabe 2</label>
|
||||
<input type="text" name="a_2" id="edit_a_2" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Aufgabe 3</label>
|
||||
<input type="text" name="a_3" id="edit_a_3" class="form-control">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Aufgabe 4</label>
|
||||
<input type="text" name="a_4" id="edit_a_4" class="form-control">
|
||||
</div>
|
||||
<div style="display:flex; gap:10px; margin-top:20px">
|
||||
<button type="submit" class="btn btn-start">💾 Speichern</button>
|
||||
<button type="button" class="btn btn-stop"
|
||||
onclick="document.getElementById('editModal').style.display='none'">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openEdit(e) {
|
||||
document.getElementById('edit_id').value = e.ID ?? '';
|
||||
document.getElementById('edit_datum').value = e.DATUM ?? '';
|
||||
document.getElementById('edit_begin').value = e.BEGIN ?? '';
|
||||
document.getElementById('edit_ende').value = e.ENDE ?? '';
|
||||
document.getElementById('edit_a_1').value = e.A_1 ?? '';
|
||||
document.getElementById('edit_a_2').value = e.A_2 ?? '';
|
||||
document.getElementById('edit_a_3').value = e.A_3 ?? '';
|
||||
document.getElementById('edit_a_4').value = e.A_4 ?? '';
|
||||
document.getElementById('editModal').style.display = 'flex';
|
||||
}
|
||||
|
||||
// Modal schließen bei Klick außerhalb
|
||||
document.getElementById('editModal').addEventListener('click', function(e) {
|
||||
if (e.target === this) this.style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.status-card {
|
||||
padding: 25px 30px;
|
||||
border-radius: 10px;
|
||||
border: 2px solid var(--border);
|
||||
background: var(--sidebar);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.status-card.running { border-color: #2ecc71; }
|
||||
.status-card.stopped { border-color: var(--border); }
|
||||
|
||||
.status-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #95a5a6;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.running .status-dot {
|
||||
background: #2ecc71;
|
||||
animation: pulse 1.2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
|
||||
.aufgaben-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.aufgabe-input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.btn-start { background: #2ecc71; color: white; }
|
||||
.btn-stop { background: #e74c3c; color: white; }
|
||||
.btn:hover { opacity: 0.85; }
|
||||
|
||||
.card {
|
||||
background: var(--sidebar);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
text-align: left;
|
||||
padding: 13px 15px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.data-table th {
|
||||
background: rgba(0,0,0,0.04);
|
||||
font-weight: bold;
|
||||
}
|
||||
.data-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
.data-table tr:hover td {
|
||||
background: rgba(0,0,0,0.02);
|
||||
}
|
||||
|
||||
.badge-running {
|
||||
color: #2ecc71;
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
background: rgba(0,123,255,0.1);
|
||||
color: var(--accent);
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
font-size: 0.78rem;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.btn-icon:hover {
|
||||
background: rgba(0,0,0,0.07);
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.55);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 200;
|
||||
}
|
||||
.modal-box {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
width: 520px;
|
||||
max-width: 95vw;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.form-group {
|
||||
flex: 1;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
box-sizing: border-box;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 2px rgba(0,123,255,0.2);
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +0,0 @@
|
||||
<h2><?= htmlspecialchars($title) ?></h2>
|
||||
<div class="card">
|
||||
<p><?= htmlspecialchars($message) ?></p>
|
||||
<p>Hier kannst du nun dein Projekt aufbauen.</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--card-border);
|
||||
}
|
||||
</style>
|
||||
@@ -14,5 +14,5 @@ return [
|
||||
'template_form' => ['label' => '📝 Form Template'],
|
||||
'template_table' => ['label' => '📊 Table Template'],
|
||||
'docs' => ['label' => '📚 Dokumentation'],
|
||||
'zeiterfassung' => ['label' => 'Zeiterfassung'],
|
||||
'webtime' => ['label' => '⏱️ WebTime'],
|
||||
];
|
||||
@@ -23,8 +23,8 @@ $router->add('template_download', \MVC\Controller\TemplateController::class, 'do
|
||||
$router->add('template_form', \MVC\Controller\TemplateController::class, 'data_form', ['auth']);
|
||||
$router->add('template_table', \MVC\Controller\TemplateController::class, 'data_table', ['auth']);
|
||||
$router->add('download_file', \MVC\Controller\TemplateController::class, 'download_file', ['auth']);
|
||||
$router->add('webtime',\MVC\Controller\WebtimeController::class, 'webtime', ['auth']);
|
||||
|
||||
// API Routes (Path-based)
|
||||
$router->add('api/v1/test', \MVC\Controller\ApiController::class, 'get');
|
||||
$router->add('api/v1/become', \MVC\Controller\ApiController::class, 'become');
|
||||
$router->add('zeiterfassung', \MVC\Controller\ZeiterfassungController::class, 'zeiterfassung');
|
||||
Reference in new issue
Block a user