diff --git a/web/admin.css b/web/admin.css new file mode 100644 index 0000000..8141b58 --- /dev/null +++ b/web/admin.css @@ -0,0 +1,595 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background-color: #f5f5f5; + color: #333; + padding-bottom: 40px; /* Make room for footer */ +} + +.top-bar { + background: linear-gradient(135deg, #2c3e50, #3498db); + color: white; + padding: 0.5rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + position: relative; + z-index: 100; +} + +.title h1 { + margin: 0; + font-size: 1.5rem; +} + +.menu-buttons { + display: flex; + gap: 1rem; + align-items: center; +} + +.top-bar .user-info { + font-size: 0.9rem; + opacity: 0.9; + display: flex; + align-items: center; + gap: 0.3rem; +} + +.container { + max-width: 1400px; + margin: 0 auto; + padding: 2rem; +} + +.btn { + padding: 0.7rem 1.5rem; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 0.9rem; + font-weight: 500; + transition: all 0.3s ease; + text-decoration: none; + display: inline-block; +} + +.btn-primary { + background-color: #3498db; + color: white; +} + +.btn-primary:hover { + background-color: #2980b9; +} + +.btn-success { + background-color: #27ae60; + color: white; +} + +.btn-success:hover { + background-color: #229954; +} + +.btn-warning { + background-color: #f39c12; + color: white; +} + +.btn-warning:hover { + background-color: #e67e22; +} + +.btn-info { + background-color: #3498db; + color: white; +} + +.btn-info:hover { + background-color: #2980b9; +} + +.btn-danger { + background-color: #e74c3c; + color: white; +} + +.btn-danger:hover { + background-color: #c0392b; +} + +.btn-icon { + padding: 0.3rem 0.6rem; + font-size: 0.8rem; + min-width: auto; +} + +.btn-icon:hover { + transform: scale(1.05); +} + +.filter-group { + display: flex; + gap: 0.5rem; + align-items: center; +} + +.filter-group label { + font-weight: 500; + color: #555; +} + +.filter-group select, .filter-group input { + padding: 0.5rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 0.9rem; +} + +.ppr-table { + background: white; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + overflow: hidden; +} + +.table-header { + background: #34495e; + color: white; + padding: 1rem; + font-weight: 500; +} + +.table-header-collapsible { + background: #34495e; + color: white; + padding: 1rem; + font-weight: 500; + cursor: pointer; + user-select: none; + display: flex; + justify-content: space-between; + align-items: center; +} + +.table-header-collapsible:hover { + background: #3d5a6e; +} + +.collapse-icon { + transition: transform 0.3s ease; + font-size: 1.2rem; +} + +.collapse-icon.collapsed { + transform: rotate(-90deg); +} + +.footer-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: #34495e; + color: white; + padding: 0.5rem 2rem; + text-align: center; + font-size: 0.85rem; + z-index: 50; + box-shadow: 0 -2px 10px rgba(0,0,0,0.1); +} + +.loading { + text-align: center; + padding: 2rem; + color: #666; +} + +.spinner { + border: 3px solid #f3f3f3; + border-top: 3px solid #3498db; + border-radius: 50%; + width: 30px; + height: 30px; + animation: spin 1s linear infinite; + margin: 0 auto 1rem; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, td { + padding: 0.5rem; + text-align: left; + border-bottom: 1px solid #eee; + font-size: 1.4rem; +} + +th { + background-color: #f8f9fa; + font-weight: 600; + color: #495057; + position: sticky; + top: 0; +} + +tbody tr { + cursor: pointer; + transition: background-color 0.2s ease; +} + +tbody tr:hover { + background-color: #f8f9fa; +} + +.status { + display: inline-block; + padding: 0.3rem 0.6rem; + border-radius: 12px; + font-size: 0.8rem; + font-weight: 600; + text-transform: uppercase; +} + +.status.new { background: #e3f2fd; color: #1565c0; } +.status.confirmed { background: #e8f5e8; color: #2e7d32; } +.status.landed { background: #fff3e0; color: #ef6c00; } +.status.departed { background: #fce4ec; color: #c2185b; } +.status.canceled { background: #ffebee; color: #d32f2f; } +.status.deleted { background: #f3e5f5; color: #7b1fa2; } + +.no-data { + text-align: center; + padding: 3rem; + color: #666; +} + +.notes-indicator { + display: inline-block; + background-color: #ffc107; + color: #856404; + font-size: 0.8rem; + padding: 2px 6px; + border-radius: 10px; + margin-left: 5px; + cursor: help; + font-weight: 600; +} + +.notes-tooltip { + position: relative; +} + +.notes-tooltip .tooltip-text { + visibility: hidden; + width: 300px; + background-color: #333; + color: #fff; + text-align: left; + border-radius: 6px; + padding: 8px; + position: fixed; + z-index: 10000; + opacity: 0; + transition: opacity 0.3s; + font-size: 0.9rem; + line-height: 1.4; + box-shadow: 0 4px 8px rgba(0,0,0,0.3); + pointer-events: none; +} + +.notes-tooltip .tooltip-text::after { + content: ""; + position: absolute; + top: 50%; + left: -5px; + margin-top: -5px; + border-width: 5px; + border-style: solid; + border-color: transparent #333 transparent transparent; +} + +.notes-tooltip:hover .tooltip-text { + visibility: visible; + opacity: 1; +} + +/* Modal Styles */ +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); +} + +.modal-content { + background-color: white; + margin: 5% auto; + padding: 0; + border-radius: 8px; + width: 90%; + max-width: 800px; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 10px 30px rgba(0,0,0,0.3); +} + +.modal-header { + background: #34495e; + color: white; + padding: 1rem 1.5rem; + border-radius: 8px 8px 0 0; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modal-header h2 { + margin: 0; + font-size: 1.3rem; +} + +.close { + color: white; + font-size: 1.5rem; + font-weight: bold; + cursor: pointer; + border: none; + background: none; +} + +.close:hover { + opacity: 0.7; +} + +.modal-body { + padding: 1.5rem; +} + +.form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 1rem; +} + +.form-group { + display: flex; + flex-direction: column; +} + +.form-group.full-width { + grid-column: 1 / -1; +} + +.form-group label { + font-weight: 600; + margin-bottom: 0.3rem; + color: #555; +} + +.form-group input, .form-group select, .form-group textarea { + padding: 0.6rem; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 0.9rem; +} + +.form-group input:focus, .form-group select:focus, .form-group textarea:focus { + outline: none; + border-color: #3498db; + box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2); +} + +#login-form .form-group { + margin-bottom: 1rem; +} + +#login-form .form-group input { + width: 100%; +} + +#login-error { + background-color: #ffebee; + border: 1px solid #ffcdd2; + border-radius: 4px; + padding: 0.8rem; + font-size: 0.9rem; +} + +.form-actions { + display: flex; + gap: 1rem; + justify-content: space-between; + padding-top: 1rem; + border-top: 1px solid #eee; +} + +.journal-section { + margin-top: 2rem; + padding-top: 1rem; + border-top: 1px solid #eee; +} + +.journal-entries { + max-height: 200px; + overflow-y: auto; + border: 1px solid #eee; + border-radius: 4px; + padding: 1rem; + background-color: #f9f9f9; +} + +.journal-entry { + margin-bottom: 0.8rem; + padding-bottom: 0.8rem; + border-bottom: 1px solid #ddd; +} + +.journal-entry:last-child { + border-bottom: none; + margin-bottom: 0; +} + +.journal-meta { + font-size: 0.8rem; + color: #666; + margin-bottom: 0.3rem; +} + +.journal-text { + font-size: 0.9rem; + color: #333; +} + +.quick-actions { + display: flex; + gap: 0.5rem; + margin-bottom: 1rem; +} + +/* Aircraft Lookup Styles */ +#aircraft-lookup-results { + margin-top: 0.5rem; + padding: 0.5rem; + background-color: #f8f9fa; + border-radius: 4px; + font-size: 0.9rem; + min-height: 20px; + border: 1px solid #e9ecef; +} + +.aircraft-match { + padding: 0.3rem; + background-color: #e8f5e8; + border: 1px solid #c3e6c3; + border-radius: 4px; + font-family: 'Courier New', monospace; + font-weight: bold; +} + +.aircraft-no-match { + color: #6c757d; + font-style: italic; +} + +.aircraft-searching { + color: #007bff; +} + +/* Airport Lookup Styles */ +#arrival-airport-lookup-results, #departure-airport-lookup-results, #local-out-to-lookup-results { + margin-top: 0.5rem; + padding: 0.5rem; + background-color: #f8f9fa; + border-radius: 4px; + font-size: 0.9rem; + min-height: 20px; + border: 1px solid #e9ecef; +} + +.airport-match { + padding: 0.3rem; + background-color: #e8f5e8; + border: 1px solid #c3e6c3; + border-radius: 4px; + font-family: 'Courier New', monospace; + font-weight: bold; +} + +.airport-no-match { + color: #6c757d; + font-style: italic; +} + +.airport-searching { + color: #007bff; +} + +.airport-list { + max-height: 200px; + overflow-y: auto; + border: 1px solid #dee2e6; + border-radius: 4px; + background-color: white; +} + +.airport-option { + padding: 0.5rem; + border-bottom: 1px solid #f0f0f0; + cursor: pointer; + transition: background-color 0.2s ease; + display: flex; + justify-content: space-between; + align-items: center; +} + +.airport-option:hover { + background-color: #f8f9fa; +} + +.airport-option:last-child { + border-bottom: none; +} + +.airport-code { + font-family: 'Courier New', monospace; + font-weight: bold; + color: #495057; +} + +.airport-name { + color: #6c757d; + font-size: 0.85rem; +} + +.airport-location { + color: #868e96; + font-size: 0.8rem; + font-style: italic; +} + +.notification { + position: fixed; + top: 20px; + right: 20px; + background-color: #27ae60; + color: white; + padding: 12px 20px; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0,0,0,0.2); + z-index: 10000; + opacity: 0; + transform: translateY(-20px); + transition: all 0.3s ease; + font-weight: 500; + pointer-events: none; +} + +.notification.show { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} + +.notification.error { + background-color: #e74c3c; +} diff --git a/web/admin.html b/web/admin.html index 6905d32..40aa51b 100644 --- a/web/admin.html +++ b/web/admin.html @@ -4,624 +4,7 @@ PPR Admin Interface - +
@@ -3475,87 +2858,6 @@ } } - function clearArrivalAirportLookup() { - document.getElementById('arrival-airport-lookup-results').innerHTML = ''; - } - - function clearDepartureAirportLookup() { - document.getElementById('departure-airport-lookup-results').innerHTML = ''; - } - - function clearLocalOutToAirportLookup() { - document.getElementById('local-out-to-lookup-results').innerHTML = ''; - } - - // Airport lookup for Book Out modal departure field - function handleLocalOutToAirportLookup(value) { - if (!value.trim()) { - clearLocalOutToAirportLookup(); - return; - } - performLocalOutToAirportLookup(value); - } - - async function performLocalOutToAirportLookup(codeOrName) { - try { - const cleanInput = codeOrName.trim(); - - if (cleanInput.length < 2) { - clearLocalOutToAirportLookup(); - return; - } - - // Call the airport lookup API using same endpoint as PPR modal - const response = await authenticatedFetch(`/api/v1/airport/lookup/${encodeURIComponent(cleanInput)}`); - - if (!response.ok) { - throw new Error('Failed to fetch airport data'); - } - - const matches = await response.json(); - displayLocalOutToAirportLookupResults(matches, cleanInput); - - } catch (error) { - console.error('Local out-to airport lookup error:', error); - document.getElementById('local-out-to-lookup-results').innerHTML = - '
Lookup failed - will use as entered
'; - } - } - - function displayLocalOutToAirportLookupResults(matches, searchTerm) { - const resultsDiv = document.getElementById('local-out-to-lookup-results'); - - if (!matches || matches.length === 0) { - resultsDiv.innerHTML = '
No matches found - will use as entered
'; - } else { - // Show matches as clickable options (single or multiple) - const matchText = matches.length === 1 ? 'Match found - click to select:' : 'Multiple matches found - select one:'; - const listHtml = matches.map(airport => ` -
-
-
${airport.icao}
-
${airport.name}
- ${airport.city ? `
${airport.city}, ${airport.country}
` : ''} -
-
- `).join(''); - - resultsDiv.innerHTML = ` -
- ${matchText} -
-
- ${listHtml} -
- `; - } - } - - function selectLocalOutToAirport(icaoCode) { - document.getElementById('local_out_to').value = icaoCode; - clearLocalOutToAirportLookup(); - } - // Airport selection functions function selectArrivalAirport(icaoCode) { document.getElementById('in_from').value = icaoCode;