mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-04 20:42:59 +00:00
etst
This commit is contained in:
+5
-144
@@ -1144,49 +1144,7 @@
|
|||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h4 class="font-medium text-gray-700 mb-3">Přednastavené aplikace</h4>
|
<h4 class="font-medium text-gray-700 mb-3">Přednastavené aplikace</h4>
|
||||||
<div id="hardcodedAppsList" class="space-y-4">
|
<div id="hardcodedAppsList" class="space-y-4">
|
||||||
<!-- Reservations Management Section -->
|
<!-- Hardcoded apps will be loaded here -->
|
||||||
<div class="card">
|
|
||||||
<h3>Rezervace vozidel</h3>
|
|
||||||
<div class="flex flex-col space-y-4">
|
|
||||||
<!-- Filters -->
|
|
||||||
<div class="flex flex-wrap gap-4 mb-4">
|
|
||||||
<div class="flex-1 max-w-sm">
|
|
||||||
<label for="vehicleFilter" class="block text-sm font-medium text-gray-700 mb-1">Vozidlo</label>
|
|
||||||
<select id="vehicleFilter" class="form-control w-full" onchange="filterReservations()">
|
|
||||||
<option value="">Všechna vozidla</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 max-w-sm">
|
|
||||||
<label for="dateFilter" class="block text-sm font-medium text-gray-700 mb-1">Datum</label>
|
|
||||||
<input type="date" id="dateFilter" class="form-control w-full" onchange="filterReservations()">
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 max-w-sm">
|
|
||||||
<button onclick="exportReservations()" class="btn btn-primary w-full">
|
|
||||||
<i class="fas fa-file-excel mr-2"></i>Export do Excelu
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reservations Table -->
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<table id="reservationsTable" class="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead class="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Jméno řidiče</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Vozidlo</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Od</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Do</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Účel</th>
|
|
||||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white divide-y divide-gray-200">
|
|
||||||
<!-- Reservations will be populated by JavaScript -->
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-center text-gray-500 py-4">Načítám přednastavené aplikace...</div>
|
<div class="text-center text-gray-500 py-4">Načítám přednastavené aplikace...</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -4599,25 +4557,13 @@ async function loadReservations() {
|
|||||||
const tbody = document.querySelector('#reservationsTable tbody');
|
const tbody = document.querySelector('#reservationsTable tbody');
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/reservations');
|
const response = await fetch('/api/reservations');
|
||||||
if (!response.ok) {
|
if (!response.ok) throw new Error('Failed to load reservations');
|
||||||
throw new Error('Failed to load reservations');
|
|
||||||
}
|
|
||||||
|
|
||||||
const reservations = await response.json();
|
const reservations = await response.json();
|
||||||
// Transform data to match frontend format
|
window.allReservations = reservations; // Store for filtering
|
||||||
const transformedReservations = reservations.map(res => ({
|
|
||||||
id: res.ID,
|
|
||||||
driverName: res.DriverName,
|
|
||||||
vehicle: res.Vehicle,
|
|
||||||
start: new Date(res.StartDate + 'T' + res.StartTime).toISOString(),
|
|
||||||
end: new Date(res.EndDate + 'T' + res.EndTime).toISOString(),
|
|
||||||
purpose: res.Purpose
|
|
||||||
}));
|
|
||||||
|
|
||||||
window.allReservations = transformedReservations; // Store for filtering
|
displayReservations(reservations);
|
||||||
|
updateVehicleFilter(reservations);
|
||||||
displayReservations(transformedReservations);
|
|
||||||
updateVehicleFilter(transformedReservations);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading reservations:', error);
|
console.error('Error loading reservations:', error);
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
@@ -5009,91 +4955,6 @@ async function saveReservation(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to load and display reservations
|
|
||||||
function loadReservations() {
|
|
||||||
fetch('/api/reservations')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const reservations = data.reservations || [];
|
|
||||||
displayReservations(reservations);
|
|
||||||
updateVehicleFilter(reservations);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error loading reservations:', error);
|
|
||||||
showNotification('Nepodařilo se načíst rezervace', 'error');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to display reservations
|
|
||||||
function displayReservations(reservations) {
|
|
||||||
const reservationsContainer = document.getElementById('reservationsContainer');
|
|
||||||
if (!reservationsContainer) return;
|
|
||||||
|
|
||||||
// Clear existing content
|
|
||||||
reservationsContainer.innerHTML = '';
|
|
||||||
|
|
||||||
if (reservations.length === 0) {
|
|
||||||
// Show no reservations message
|
|
||||||
const noReservations = document.createElement('div');
|
|
||||||
noReservations.className = 'text-center py-6 text-gray-500';
|
|
||||||
noReservations.innerHTML = 'Zatím nebyly vytvořeny žádné rezervace.';
|
|
||||||
reservationsContainer.appendChild(noReservations);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort reservations by date
|
|
||||||
reservations.sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
||||||
|
|
||||||
// Add table header
|
|
||||||
const table = document.createElement('table');
|
|
||||||
table.className = 'min-w-full divide-y divide-gray-200';
|
|
||||||
const thead = document.createElement('thead');
|
|
||||||
thead.className = 'bg-gray-50';
|
|
||||||
const headerRow = document.createElement('tr');
|
|
||||||
|
|
||||||
const headers = ['Datum', 'Čas', 'Vozidlo', 'Jméno', 'Telefon', 'Email', 'Délka', 'Akce'];
|
|
||||||
headers.forEach(header => {
|
|
||||||
const th = document.createElement('th');
|
|
||||||
th.className = 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider';
|
|
||||||
th.textContent = header;
|
|
||||||
headerRow.appendChild(th);
|
|
||||||
});
|
|
||||||
thead.appendChild(headerRow);
|
|
||||||
table.appendChild(thead);
|
|
||||||
|
|
||||||
// Add table body
|
|
||||||
const tbody = document.createElement('tbody');
|
|
||||||
tbody.className = 'bg-white divide-y divide-gray-200';
|
|
||||||
|
|
||||||
// Add reservations
|
|
||||||
reservations.forEach(reservation => {
|
|
||||||
const row = document.createElement('tr');
|
|
||||||
row.className = 'hover:bg-gray-50';
|
|
||||||
|
|
||||||
// Add cells
|
|
||||||
const cells = [
|
|
||||||
formatDateTime(reservation.date, reservation.time),
|
|
||||||
reservation.vehicle,
|
|
||||||
reservation.name,
|
|
||||||
reservation.phone,
|
|
||||||
reservation.email,
|
|
||||||
calculateDuration(reservation),
|
|
||||||
'<button onclick="editReservation(\'' + reservation.id + '\')" class="text-blue-600 hover:text-blue-900">Upravit</button> '
|
|
||||||
];
|
|
||||||
|
|
||||||
cells.forEach(cellContent => {
|
|
||||||
const cell = document.createElement('td');
|
|
||||||
cell.className = 'px-6 py-4 whitespace-nowrap text-sm text-gray-900';
|
|
||||||
cell.innerHTML = cellContent;
|
|
||||||
row.appendChild(cell);
|
|
||||||
});
|
|
||||||
|
|
||||||
tbody.appendChild(row);
|
|
||||||
});
|
|
||||||
|
|
||||||
table.appendChild(tbody);
|
|
||||||
reservationsContainer.appendChild(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listeners when the page loads
|
// Add event listeners when the page loads
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|||||||
@@ -145,20 +145,8 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
|
|||||||
stats.TodayVisits++
|
stats.TodayVisits++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract IP address (remove port)
|
// Get visitor ID (using IP and User-Agent)
|
||||||
ip := r.RemoteAddr
|
visitorID := fmt.Sprintf("%s-%s", r.RemoteAddr, r.UserAgent())
|
||||||
if strings.Contains(ip, ":") {
|
|
||||||
// Split at the last colon to remove port
|
|
||||||
parts := strings.Split(ip, ":")
|
|
||||||
ip = strings.Join(parts[:len(parts)-1], ":")
|
|
||||||
// Remove IPv6 brackets if present
|
|
||||||
if strings.HasPrefix(ip, "[") && strings.HasSuffix(ip, "]") {
|
|
||||||
ip = ip[1 : len(ip)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get visitor ID (using cleaned IP and User-Agent)
|
|
||||||
visitorID := fmt.Sprintf("%s-%s", ip, r.UserAgent())
|
|
||||||
|
|
||||||
// Update or create visitor stats
|
// Update or create visitor stats
|
||||||
if visitor, exists := stats.UniqueVisitors[visitorID]; exists {
|
if visitor, exists := stats.UniqueVisitors[visitorID]; exists {
|
||||||
@@ -369,36 +357,6 @@ type Reservation struct {
|
|||||||
Purpose string `json:"purpose,omitempty"`
|
Purpose string `json:"purpose,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getReservations(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Load reservations from JSON file
|
|
||||||
data, err := os.ReadFile("data/reservations.json")
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// Return empty array if file doesn't exist
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write([]byte("[]"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("Error reading reservations: %v", err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var reservations []Reservation
|
|
||||||
if err := json.Unmarshal(data, &reservations); err != nil {
|
|
||||||
log.Printf("Error unmarshaling reservations: %v", err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
if err := json.NewEncoder(w).Encode(reservations); err != nil {
|
|
||||||
log.Printf("Error encoding reservations: %v", err)
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
|
|
||||||
@@ -415,9 +373,6 @@ func main() {
|
|||||||
// Visitor tracking endpoints
|
// Visitor tracking endpoints
|
||||||
r.HandleFunc("/api/track-visit", trackVisit).Methods("GET")
|
r.HandleFunc("/api/track-visit", trackVisit).Methods("GET")
|
||||||
r.HandleFunc("/api/visitor-stats", getVisitorStats).Methods("GET")
|
r.HandleFunc("/api/visitor-stats", getVisitorStats).Methods("GET")
|
||||||
|
|
||||||
// Reservations endpoints
|
|
||||||
r.HandleFunc("/api/reservations", getReservations).Methods("GET")
|
|
||||||
|
|
||||||
// Set up reverse proxy to kontakt service
|
// Set up reverse proxy to kontakt service
|
||||||
kontaktURL, _ := url.Parse("http://webportal:8080")
|
kontaktURL, _ := url.Parse("http://webportal:8080")
|
||||||
|
|||||||
Reference in New Issue
Block a user