Projektsuche Implementiert mit anbindung an INFRA
This commit is contained in:
@@ -392,6 +392,55 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mssql-config-section" style="margin-top: 40px;">
|
||||
<div class="collapsible-header" onclick="toggleMssqlSection()" style="cursor: pointer; padding: 15px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<h2 style="margin: 0;">MSSQL-Projektsuche Konfiguration</h2>
|
||||
<span id="mssqlToggleIcon" style="font-size: 18px; transition: transform 0.3s;">▼</span>
|
||||
</div>
|
||||
|
||||
<div id="mssqlContent" style="display: none; padding: 20px; border: 1px solid #ddd; border-top: none; border-radius: 0 0 4px 4px; background-color: #fff;">
|
||||
<div class="mssql-config-form">
|
||||
<h3>MSSQL-Verbindungsdaten</h3>
|
||||
<p style="margin-bottom: 20px; color: #666;">
|
||||
Konfigurieren Sie hier die Verbindung zur MSSQL-Datenbank, aus der die Projektnummern ermittelt werden
|
||||
(z.B. Tabelle <code>infra.dbo.KKOPF</code> und <code>KUNDE</code>).
|
||||
</p>
|
||||
<form id="mssqlConfigForm">
|
||||
<div class="form-group">
|
||||
<label for="mssqlServer">Server</label>
|
||||
<input type="text" id="mssqlServer" name="server" placeholder="z.B. SERVER\\INSTANZ oder server.domain.local">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mssqlDatabase">Datenbankname</label>
|
||||
<input type="text" id="mssqlDatabase" name="database" placeholder="z.B. infra">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mssqlUsername">Benutzername</label>
|
||||
<input type="text" id="mssqlUsername" name="username" placeholder="Datenbank-Benutzer">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="mssqlPassword">Passwort</label>
|
||||
<input type="password" id="mssqlPassword" name="password" placeholder="Leer lassen, um aktuelles Passwort zu behalten">
|
||||
</div>
|
||||
|
||||
<% if (mssqlConfig && mssqlConfig.updated_at) { %>
|
||||
<p style="margin-top: 10px; color: #666;">
|
||||
<strong>Zuletzt geändert:</strong>
|
||||
<%= new Date(mssqlConfig.updated_at).toLocaleString('de-DE') %>
|
||||
</p>
|
||||
<% } %>
|
||||
|
||||
<button type="submit" class="btn btn-primary">MSSQL-Konfiguration speichern</button>
|
||||
<button type="button" id="mssqlTestConnectionBtn" class="btn btn-secondary" style="margin-left: 10px;">Verbindung testen</button>
|
||||
<span id="mssqlTestStatus" style="margin-left: 10px;"></span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -454,6 +503,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMssqlSection() {
|
||||
const content = document.getElementById('mssqlContent');
|
||||
const icon = document.getElementById('mssqlToggleIcon');
|
||||
|
||||
if (content.style.display === 'none') {
|
||||
content.style.display = 'block';
|
||||
icon.style.transform = 'rotate(180deg)';
|
||||
} else {
|
||||
content.style.display = 'none';
|
||||
icon.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
}
|
||||
|
||||
// Rollenwechsel-Handler
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const roleSwitcher = document.getElementById('roleSwitcher');
|
||||
|
||||
169
views/project-search.ejs
Normal file
169
views/project-search.ejs
Normal file
@@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Projektsuche</title>
|
||||
<link rel="icon" type="image/png" href="/images/favicon.png">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<%- include('header') %>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<img src="/images/header.png" alt="Logo" class="navbar-logo">
|
||||
<h1>Stundenerfassung - Projektsuche</h1>
|
||||
</div>
|
||||
<div class="nav-right">
|
||||
<span><%= user.firstname %> <%= user.lastname %></span>
|
||||
<a href="/logout" class="btn btn-logout">Abmelden</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" style="margin-top: 30px;">
|
||||
<h2>Projektsuche</h2>
|
||||
<p>
|
||||
Suchen Sie nach Projekten über die Beschreibung (<code>Proj</code>).
|
||||
Die Projektnummer stammt aus <code>Auftrag</code>, die Kundenbezeichnung aus <code>KUNDE.Bez</code>.
|
||||
</p>
|
||||
|
||||
<% if (error) { %>
|
||||
<div class="alert alert-danger"><%= error %></div>
|
||||
<% } %>
|
||||
|
||||
<form id="searchForm" class="form-inline" style="margin-top: 15px; margin-bottom: 15px;">
|
||||
<label for="searchTerm" style="margin-right: 10px;">Suchbegriff in Projektbeschreibung:</label>
|
||||
<input type="text" id="searchTerm" name="term" required style="min-width: 250px; margin-right: 10px;">
|
||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||
</form>
|
||||
|
||||
<div id="message" style="margin-top: 10px; color: #c00;"></div>
|
||||
|
||||
<table id="resultsTable" style="width:100%; margin-top:1rem; border-collapse: collapse; display:none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="border-bottom:1px solid #ccc; text-align:left;">Projektnummer (Auftrag)</th>
|
||||
<th style="border-bottom:1px solid #ccc; text-align:left;">Beschreibung (Proj)</th>
|
||||
<th style="border-bottom:1px solid #ccc; text-align:left;">Seriennummer</th>
|
||||
<th style="border-bottom:1px solid #ccc; text-align:left;">Kundennummer (Knd)</th>
|
||||
<th style="border-bottom:1px solid #ccc; text-align:left;">Kundenbezeichnung (Bez)</th>
|
||||
<th style="border-bottom:1px solid #ccc; text-align:left;">Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById('searchForm');
|
||||
const messageEl = document.getElementById('message');
|
||||
const table = document.getElementById('resultsTable');
|
||||
const tbody = table.querySelector('tbody');
|
||||
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const term = document.getElementById('searchTerm').value.trim();
|
||||
messageEl.style.color = '#c00';
|
||||
messageEl.textContent = '';
|
||||
tbody.innerHTML = '';
|
||||
table.style.display = 'none';
|
||||
|
||||
if (!term) {
|
||||
messageEl.textContent = 'Bitte einen Suchbegriff eingeben.';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({ term });
|
||||
const response = await fetch('/api/projects/search?' + params.toString(), {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
messageEl.textContent = result.error || 'Fehler bei der Projektsuche.';
|
||||
return;
|
||||
}
|
||||
|
||||
const rows = result.results || [];
|
||||
if (rows.length === 0) {
|
||||
messageEl.textContent = 'Keine Projekte gefunden.';
|
||||
return;
|
||||
}
|
||||
|
||||
rows.forEach(row => {
|
||||
const tr = document.createElement('tr');
|
||||
const auftrag = row.auftrag || '';
|
||||
const proj = row.proj || '';
|
||||
const such = row.such || '';
|
||||
const knd = row.knd || '';
|
||||
const bez = row.bez || '';
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${auftrag}</td>
|
||||
<td>${proj}</td>
|
||||
<td>${such}</td>
|
||||
<td>${knd}</td>
|
||||
<td>${bez}</td>
|
||||
<td><button type="button" class="btn btn-secondary btn-sm copy-btn" data-auftrag="${auftrag}">Kopieren</button></td>
|
||||
`;
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
table.style.display = '';
|
||||
} catch (err) {
|
||||
console.error('Fehler bei der Projektsuche:', err);
|
||||
messageEl.textContent = 'Fehler bei der Projektsuche. Bitte später erneut versuchen.';
|
||||
}
|
||||
});
|
||||
|
||||
function copyToClipboard(text) {
|
||||
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
||||
return navigator.clipboard.writeText(text);
|
||||
}
|
||||
var ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.setAttribute('readonly', '');
|
||||
ta.style.position = 'absolute';
|
||||
ta.style.left = '-9999px';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
} finally {
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
}
|
||||
|
||||
tbody.addEventListener('click', (ev) => {
|
||||
const btn = ev.target.closest('button.copy-btn');
|
||||
if (!btn) return;
|
||||
const value = btn.getAttribute('data-auftrag');
|
||||
if (!value) return;
|
||||
|
||||
copyToClipboard(value).then(() => {
|
||||
messageEl.style.color = 'green';
|
||||
messageEl.textContent = 'Projektnummer wurde in die Zwischenablage kopiert.';
|
||||
setTimeout(() => {
|
||||
messageEl.textContent = '';
|
||||
messageEl.style.color = '#c00';
|
||||
}, 2000);
|
||||
}).catch(() => {
|
||||
messageEl.textContent = 'Kopieren nicht möglich.';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<%- include('footer') %>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user