Compare commits
108
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0914b60a6 | ||
|
|
0541557ae9 | ||
|
|
992ac5eb6a | ||
|
|
d37937f4d8 | ||
|
|
e98dbdc77b | ||
|
|
aa4829343e | ||
|
|
523400ac0e | ||
|
|
2064546b19 | ||
|
|
7d8a891798 | ||
|
|
ddbf5b697f | ||
|
|
d71e2c1e2d | ||
|
|
643be91c57 | ||
|
|
f1b58a3399 | ||
|
|
62dadd175c | ||
|
|
3c9eb7091f | ||
|
|
7abcac5f04 | ||
|
|
61ada9f2e3 | ||
|
|
5759f999c5 | ||
|
|
7d03579d31 | ||
|
|
742c5d5b56 | ||
|
|
20e0e09305 | ||
|
|
d8a0121ac2 | ||
|
|
0952d6de13 | ||
|
|
733fc7dc12 | ||
|
|
99f4f8a746 | ||
|
|
81ff082d71 | ||
|
|
3b7453563d | ||
|
|
9b1dfa8441 | ||
|
|
2f285f284b | ||
|
|
fe5979d4d0 | ||
|
|
950fcd6f3b | ||
|
|
3843677903 | ||
|
|
ca867e91d0 | ||
|
|
ad763caaf5 | ||
|
|
9ca5e7617c | ||
|
|
37871de69f | ||
|
|
0431257a44 | ||
|
|
33e4388b20 | ||
|
|
5479d0e364 | ||
|
|
206d56b10c | ||
|
|
f0f7c16abc | ||
|
|
859f67ca02 | ||
|
|
69199c78eb | ||
|
|
e0e9bca81a | ||
|
|
232cc27ce3 | ||
|
|
d727452dd8 | ||
|
|
636f6b5c37 | ||
|
|
62844653d2 | ||
|
|
88a95d28e3 | ||
|
|
14e01891a2 | ||
|
|
4cadcf263d | ||
|
|
1dfc6c9ac5 | ||
|
|
4e43f234c2 | ||
|
|
671688f37d | ||
|
|
1eff0a54a6 | ||
|
|
5ea0308b83 | ||
|
|
859c1563fb | ||
|
|
9c1c86a7e7 | ||
|
|
d6232f6418 | ||
|
|
878b691ab1 | ||
|
|
a7368eb18f | ||
|
|
38adea29cf | ||
|
|
f6fbc8404c | ||
|
|
e45fecf6ff | ||
|
|
5dce558640 | ||
|
|
ffb6f38af0 | ||
|
|
3e54b917ca | ||
|
|
4756d0586f | ||
|
|
86a1eecd8d | ||
|
|
9dcf0ced19 | ||
|
|
c05f9f721c | ||
|
|
04c89fb676 | ||
|
|
5160bbc5a5 | ||
|
|
e9550b9a05 | ||
|
|
c5aabcf5d1 | ||
|
|
e13ac6c503 | ||
|
|
bcaaac8a19 | ||
|
|
e2ee377e35 | ||
|
|
57813e3309 | ||
|
|
9eb06a3a16 | ||
|
|
a920fe99a8 | ||
|
|
0776ae8dd0 | ||
|
|
21fa9145cb | ||
|
|
c350f4c776 | ||
|
|
fd5f3093c6 | ||
|
|
c47723ec00 | ||
|
|
737ea6e962 | ||
|
|
0e5cdcd725 | ||
|
|
739fda06e7 | ||
|
|
3d170a38d8 | ||
|
|
03febbc877 | ||
|
|
bdc41bbd48 | ||
|
|
1f3e8228d1 | ||
|
|
dccec7642b | ||
|
|
6f74a11a79 | ||
|
|
ea51b1808a | ||
|
|
2eacb68fd6 | ||
|
|
8c53e850c1 | ||
|
|
0d9b9e7b07 | ||
|
|
9b6060d39f | ||
|
|
01d6c69b3e | ||
|
|
7c94592b2c | ||
|
|
883a1fd76f | ||
|
|
dd08ec793a | ||
|
|
24799dffbe | ||
|
|
34f464f442 | ||
|
|
d1464d874f | ||
|
|
8f5a616dd8 |
No files matched your search
@@ -1,21 +1,23 @@
|
||||
<?php
|
||||
|
||||
require(__DIR__ . '/DatabaseController.php');
|
||||
include_once(__DIR__ . '/DatabaseController.php');
|
||||
|
||||
class ApiController
|
||||
{
|
||||
|
||||
class ApiController {
|
||||
|
||||
/**
|
||||
* method for pretty jsonsuccess output
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function jsonSuccess($data) {
|
||||
public function jsonSuccess($data)
|
||||
{
|
||||
return json_encode(
|
||||
[
|
||||
'status' => 'success',
|
||||
'data' => $data,
|
||||
],
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||
);
|
||||
}
|
||||
@@ -26,13 +28,14 @@ class ApiController {
|
||||
* @param array $error
|
||||
* @return string
|
||||
*/
|
||||
public function jsonError ($error) {
|
||||
public function jsonError($error)
|
||||
{
|
||||
echo json_encode(
|
||||
[
|
||||
'status' => 'failed',
|
||||
'data' => 'none',
|
||||
'error' => $error
|
||||
],
|
||||
],
|
||||
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
|
||||
);
|
||||
}
|
||||
@@ -42,7 +45,8 @@ class ApiController {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getAuthKey () {
|
||||
private function getAuthKey()
|
||||
{
|
||||
$validTokens = DatabaseShow(
|
||||
"API_Credentials",
|
||||
"TOKEN"
|
||||
@@ -58,7 +62,8 @@ class ApiController {
|
||||
* @param string $function
|
||||
* @return void
|
||||
*/
|
||||
public function urlAuth ($token, $function = '') {
|
||||
public function urlAuth($token, $function = '')
|
||||
{
|
||||
|
||||
$validTokens = $this->getAuthKey();
|
||||
if (!in_array($token, $validTokens)) {
|
||||
@@ -90,7 +95,8 @@ class ApiController {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function requestSave($function = '') {
|
||||
public function requestSave($function = '')
|
||||
{
|
||||
DatabaseInsert(
|
||||
"API_LOGS",
|
||||
"CLIENT_IP, TOKEN, API, FUNKTION, JSON",
|
||||
@@ -106,7 +112,8 @@ class ApiController {
|
||||
* create new api token
|
||||
*
|
||||
*/
|
||||
private function createToken() {
|
||||
private function createToken()
|
||||
{
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
@@ -115,8 +122,9 @@ class ApiController {
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveToken() {
|
||||
try {
|
||||
public function saveToken()
|
||||
{
|
||||
try {
|
||||
DatabaseInsert(
|
||||
"API_Credentials",
|
||||
"TOKEN, BENUTZER",
|
||||
@@ -128,4 +136,4 @@ class ApiController {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ function db_connect() {
|
||||
]
|
||||
];
|
||||
|
||||
$enviroment = 'vpn';
|
||||
$enviroment = 'live';
|
||||
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
|
||||
+205
-17
@@ -1,10 +1,15 @@
|
||||
<?php
|
||||
include_once __DIR__ . '/../controller/DatabaseController.php';
|
||||
include_once __DIR__ . '/../controller/RoutesController.php';
|
||||
include_once __DIR__ . '/../modules/ConsoleLog.php';
|
||||
|
||||
$conn = db_connect();
|
||||
$view = $_GET['view'] ?? 'show';
|
||||
$selectedService = $_GET['service'] ?? null;
|
||||
$view = $_GET['view'] ?? 'show';
|
||||
$selectedService = $_GET['service'] ?? null;
|
||||
|
||||
// --- Suchfilter auslesen ---
|
||||
$searchWer = $_GET['search_wer'] ?? '';
|
||||
$searchWas = $_GET['search_was'] ?? '';
|
||||
|
||||
// --- LOGIK: Überstunden-Eintrag (Abbau) speichern ---
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_entry'])) {
|
||||
@@ -14,10 +19,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_entry'])) {
|
||||
$rueckgabe = $_POST['rueckgabe'];
|
||||
|
||||
DatabaseInsert(
|
||||
"Vergabe",
|
||||
"WAS, WER, WANN, RUECKGABE",
|
||||
$was,
|
||||
$wer,
|
||||
"Vergabe",
|
||||
"WAS, WER, WANN, RUECKGABE",
|
||||
$was,
|
||||
$wer,
|
||||
$wann,
|
||||
$rueckgabe
|
||||
);
|
||||
@@ -29,16 +34,47 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_entry'])) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_entry'])) {
|
||||
$id = $_POST['id'];
|
||||
|
||||
DatabaseDelete("Vergabe", "ID = ?", $id);
|
||||
ConsoleLog("$id");
|
||||
|
||||
DatabaseUpdate(
|
||||
"Vergabe",
|
||||
"STATUS = ?",
|
||||
"ID = ?",
|
||||
1,
|
||||
$id
|
||||
);
|
||||
|
||||
redirectToWebsite((new getSpecificData())->getReferer());
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Dynamic Query mit kombiniertem Suchfilter ---
|
||||
$whereClause = "STATUS IS NULL";
|
||||
if (!empty($searchWer)) {
|
||||
$whereClause .= " AND WER LIKE '%" . $conn->real_escape_string($searchWer) . "%'";
|
||||
}
|
||||
if (!empty($searchWas)) {
|
||||
$whereClause .= " AND WAS LIKE '%" . $conn->real_escape_string($searchWas) . "%'";
|
||||
}
|
||||
|
||||
$entries = DatabaseShow(
|
||||
"Vergabe"
|
||||
"Vergabe",
|
||||
"ID, WAS, WER, WANN, RUECKGABE",
|
||||
$whereClause
|
||||
);
|
||||
|
||||
$searchEntries = DatabaseShow(
|
||||
"Vergabe",
|
||||
);
|
||||
|
||||
$existingWas = [];
|
||||
$existingWer = [];
|
||||
foreach ($searchEntries as $entry) {
|
||||
if (!empty($entry['WAS'])) $existingWas[$entry['WAS']] = true;
|
||||
if (!empty($entry['WER'])) $existingWer[$entry['WER']] = true;
|
||||
}
|
||||
$existingWas = array_keys($existingWas);
|
||||
$existingWer = array_keys($existingWer);
|
||||
?>
|
||||
|
||||
<div class="arbeitsberichte-wrapper">
|
||||
@@ -55,25 +91,73 @@ $entries = DatabaseShow(
|
||||
<input type="date" name="rueckgabe" value="<?= date('Y-m-d') ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label>An wen</label>
|
||||
<input type="text" name="wer" placeholder="Name eingeben" required>
|
||||
<input type="text" name="wer" id="wer-input" placeholder="Name eingeben" required autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="wer-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label>Was</label>
|
||||
<input type="text" name="was" placeholder="Gegenstand eingeben" required>
|
||||
<input type="text" name="was" id="was-input" placeholder="Gegenstand eingeben" required autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="was-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<button type="submit" name="add_entry" class="btn-submit-vergabe">
|
||||
💾 Speichern
|
||||
Speichern
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="vergabe-form" style="margin-top: 30px;">
|
||||
<form method="GET" action="" class="vergabe-grid" id="search-form">
|
||||
<input type="hidden" name="view" value="<?= htmlspecialchars($view) ?>">
|
||||
<?php if ($selectedService): ?>
|
||||
<input type="hidden" name="service" value="<?= htmlspecialchars($selectedService) ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label for="search-wer-input">Nach Person filtern</label>
|
||||
<input type="text" name="search_wer" id="search-wer-input" value="<?= htmlspecialchars($searchWer) ?>" placeholder="Name eingeben..." autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="search-wer-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group autocomplete-wrapper">
|
||||
<label for="search-was-input">Nach Gegenstand filtern</label>
|
||||
<input type="text" name="search_was" id="search-was-input" value="<?= htmlspecialchars($searchWas) ?>" placeholder="Gegenstand eingeben..." autocomplete="off">
|
||||
<div class="autocomplete-suggestions" id="search-was-suggestions"></div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px; align-items: flex-end;">
|
||||
<button type="submit" class="btn-submit-vergabe" style="margin: 0; flex-grow: 1;">
|
||||
Suchen
|
||||
</button>
|
||||
|
||||
<?php if (!empty($searchWer) || !empty($searchWas)): ?>
|
||||
<a href="?view=<?= urlencode($view) ?><?= $selectedService ? '&service=' . urlencode($selectedService) : '' ?>" class="btn-delete" style="text-decoration: none; padding: 10px; display: inline-block; text-align: center; white-space: nowrap; margin: 0;">
|
||||
Zurücksetzen
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-container" style="margin-top: 30px;">
|
||||
<h3>Aktuelle Vergaben</h3>
|
||||
|
||||
<h3>
|
||||
<?php
|
||||
if (!empty($searchWer) && !empty($searchWas)) {
|
||||
echo "Gefiltert nach: " . htmlspecialchars($searchWer) . " ➔ " . htmlspecialchars($searchWas);
|
||||
} elseif (!empty($searchWer)) {
|
||||
echo "Vergaben für: " . htmlspecialchars($searchWer);
|
||||
} elseif (!empty($searchWas)) {
|
||||
echo "Ausgeliehene Gegenstände: " . htmlspecialchars($searchWas);
|
||||
} else {
|
||||
echo "Aktuelle Vergaben";
|
||||
}
|
||||
?>
|
||||
</h3>
|
||||
|
||||
<?php if (!empty($entries)): ?>
|
||||
<table class="db-table mobile-responsive-table">
|
||||
<thead>
|
||||
@@ -96,7 +180,7 @@ $entries = DatabaseShow(
|
||||
<form method="POST" onsubmit="return confirm('Möchtest du diesen Eintrag wirklich löschen?');" style="margin: 0;">
|
||||
<input type="hidden" name="id" value="<?= $entry['ID'] ?? '' ?>">
|
||||
<button type="submit" name="delete_entry" class="btn-delete">
|
||||
🗑️ Löschen
|
||||
Löschen
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
@@ -108,4 +192,108 @@ $entries = DatabaseShow(
|
||||
<p>Keine Einträge vorhanden.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Parameter `submitOnSelect` hinzugefügt (Standard: false)
|
||||
function setupAutocomplete(inputId, suggestionsId, data, submitOnSelect = false) {
|
||||
const input = document.getElementById(inputId);
|
||||
const box = document.getElementById(suggestionsId);
|
||||
if (!input || !box) return;
|
||||
|
||||
let items = [];
|
||||
let activeIndex = -1;
|
||||
|
||||
function render(list) {
|
||||
box.innerHTML = '';
|
||||
items = list;
|
||||
activeIndex = -1;
|
||||
if (list.length === 0) {
|
||||
box.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
list.forEach((val, i) => {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = val;
|
||||
div.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
selectItem(i);
|
||||
});
|
||||
box.appendChild(div);
|
||||
});
|
||||
box.style.display = 'block';
|
||||
}
|
||||
|
||||
function updateActive() {
|
||||
[...box.children].forEach((el, i) => {
|
||||
el.classList.toggle('active', i === activeIndex);
|
||||
});
|
||||
if (activeIndex >= 0) {
|
||||
box.children[activeIndex].scrollIntoView({
|
||||
block: 'nearest'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function selectItem(i) {
|
||||
if (items[i] !== undefined) {
|
||||
input.value = items[i];
|
||||
}
|
||||
box.style.display = 'none';
|
||||
activeIndex = -1;
|
||||
|
||||
// NEU: Wenn gewünscht, schicke das übergeordnete Formular sofort ab
|
||||
if (submitOnSelect) {
|
||||
const form = input.closest('form');
|
||||
if (form) form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
const val = input.value.toLowerCase();
|
||||
if (val.length === 0) {
|
||||
box.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
const filtered = data.filter(d => d.toLowerCase().includes(val));
|
||||
render(filtered);
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', (e) => {
|
||||
if (box.style.display !== 'block') return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
activeIndex = Math.min(activeIndex + 1, items.length - 1);
|
||||
updateActive();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
activeIndex = Math.max(activeIndex - 1, 0);
|
||||
updateActive();
|
||||
} else if (e.key === 'Tab' || e.key === 'Enter') {
|
||||
if (activeIndex >= 0) {
|
||||
e.preventDefault();
|
||||
selectItem(activeIndex);
|
||||
} else {
|
||||
box.style.display = 'none';
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
box.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target !== input) {
|
||||
box.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 1. Normales Autocomplete für das Eintragungs-Formular (schickt nicht automatisch ab)
|
||||
setupAutocomplete('was-input', 'was-suggestions', <?= json_encode($existingWas) ?>);
|
||||
setupAutocomplete('wer-input', 'wer-suggestions', <?= json_encode($existingWer) ?>);
|
||||
|
||||
// 2. Such-Autocomplete: Hier wird als 4. Parameter `true` übergeben, damit sofort abgesendet wird!
|
||||
setupAutocomplete('search-wer-input', 'search-wer-suggestions', <?= json_encode($existingWer) ?>, true);
|
||||
setupAutocomplete('search-was-input', 'search-was-suggestions', <?= json_encode($existingWas) ?>, true);
|
||||
</script>
|
||||
@@ -1401,4 +1401,39 @@ body.dark-mode .berichte-form input:focus {
|
||||
margin: 15px;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.autocomplete-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.autocomplete-suggestions {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--card-bg);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--input-border);
|
||||
border-top: none;
|
||||
max-height: 180px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.autocomplete-suggestions div {
|
||||
padding: 8px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.autocomplete-suggestions div:hover,
|
||||
.autocomplete-suggestions div.active {
|
||||
background: rgba(0, 123, 255, 0.15);
|
||||
}
|
||||
|
||||
body.dark-mode .autocomplete-suggestions div:hover,
|
||||
body.dark-mode .autocomplete-suggestions div.active {
|
||||
background: rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Erzwingt den Abbruch des Skripts, sobald ein Befehl fehlschlägt
|
||||
set -e
|
||||
|
||||
echo "🚀 Starte automatischen Merge- und Tag-Prozess..."
|
||||
|
||||
# Fix für WSL/Windows: Ignoriere Unterschiede bei Linux/Windows Dateirechten
|
||||
git config core.filemode false
|
||||
|
||||
# 1. Sicherstellen, dass wir die neuesten Stände vom Server haben
|
||||
echo "🔄 Aktualisiere lokale Branches vom Remote..."
|
||||
git fetch --all --quiet
|
||||
|
||||
# 2. Main aktualisieren
|
||||
echo "🌿 Wechsle zu main..."
|
||||
git checkout main --quiet
|
||||
git pull origin main --quiet
|
||||
|
||||
# 3. Main in Dev mergen
|
||||
echo "🔀 Merge: main -> dev..."
|
||||
git checkout dev --quiet
|
||||
git pull origin dev --quiet || true
|
||||
git merge main --no-ff -m "Automated merge from main to dev" --quiet
|
||||
git push origin dev --quiet
|
||||
|
||||
# 4. Dev in Live mergen
|
||||
echo "🔀 Merge: dev -> live..."
|
||||
git checkout live --quiet
|
||||
git pull origin live --quiet || true
|
||||
git merge dev --no-ff -m "Automated merge from dev to live" --quiet
|
||||
git push origin live --quiet
|
||||
|
||||
# 5. Tag erstellen und pushen
|
||||
TAG_NAME="v$(date +'%Y.%m.%d-%H%M')"
|
||||
|
||||
echo "🏷️ Erstelle Tag: $TAG_NAME..."
|
||||
git tag -a "$TAG_NAME" -m "Automated release tag $TAG_NAME"
|
||||
git push origin "$TAG_NAME" --quiet
|
||||
|
||||
# Zurück auf den Ausgangs-Branch wechseln
|
||||
git checkout main --quiet
|
||||
|
||||
echo ""
|
||||
echo "========================================================"
|
||||
echo "✅ FERTIG! Alles sauber durchgelaufen."
|
||||
echo "📦 Neuer Release-Tag gepusht: $TAG_NAME"
|
||||
echo "========================================================"
|
||||
Reference in new issue
Block a user