Initial commit: Parking Manager
Features: - Manager-centric parking spot management - Fair assignment algorithm (parking/presence ratio) - Presence tracking calendar - Closing days (specific & weekly recurring) - Guarantees and exclusions - Authelia/LLDAP integration for SSO Stack: - FastAPI backend - SQLite database - Vanilla JS frontend - Docker deployment
This commit is contained in:
217
frontend/pages/settings.html
Normal file
217
frontend/pages/settings.html
Normal file
@@ -0,0 +1,217 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Settings - Parking Manager</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="stylesheet" href="/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1>Parking Manager</h1>
|
||||
</div>
|
||||
<nav class="sidebar-nav"></nav>
|
||||
<div class="sidebar-footer">
|
||||
<div class="user-menu">
|
||||
<button class="user-button" id="userMenuButton">
|
||||
<div class="user-avatar">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name" id="userName">Loading...</div>
|
||||
<div class="user-role" id="userRole">-</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="user-dropdown" id="userDropdown" style="display: none;">
|
||||
<a href="/profile" class="dropdown-item">Profile</a>
|
||||
<a href="/settings" class="dropdown-item">Settings</a>
|
||||
<hr class="dropdown-divider">
|
||||
<button class="dropdown-item" id="logoutButton">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="main-content">
|
||||
<header class="page-header">
|
||||
<h2>Settings</h2>
|
||||
</header>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>Preferences</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="settingsForm">
|
||||
<div class="form-group">
|
||||
<label for="weekStartDay">Week Starts On</label>
|
||||
<select id="weekStartDay">
|
||||
<option value="0">Sunday</option>
|
||||
<option value="1">Monday</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-dark">Save Settings</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>Parking Notifications</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="notificationForm">
|
||||
<div class="form-group">
|
||||
<label class="toggle-label">
|
||||
<span>Weekly Summary</span>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="notifyWeeklyParking">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</label>
|
||||
<small class="text-muted">Receive weekly parking assignments summary every Friday at 12:00</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="toggle-label">
|
||||
<span>Daily Reminder</span>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="notifyDailyParking">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</label>
|
||||
<small class="text-muted">Receive daily parking reminder on working days</small>
|
||||
</div>
|
||||
<div class="form-group" id="dailyTimeGroup" style="margin-left: 1rem;">
|
||||
<label>Reminder Time</label>
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||||
<select id="notifyDailyHour" style="width: 80px;">
|
||||
<!-- Hours populated by JS -->
|
||||
</select>
|
||||
<span>:</span>
|
||||
<select id="notifyDailyMinute" style="width: 80px;">
|
||||
<option value="0">00</option>
|
||||
<option value="15">15</option>
|
||||
<option value="30">30</option>
|
||||
<option value="45">45</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="toggle-label">
|
||||
<span>Assignment Changes</span>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="notifyParkingChanges">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</label>
|
||||
<small class="text-muted">Receive immediate notifications when your parking assignment changes</small>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-dark">Save Notifications</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="/js/api.js"></script>
|
||||
<script src="/js/utils.js"></script>
|
||||
<script src="/js/nav.js"></script>
|
||||
<script>
|
||||
let currentUser = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
if (!api.requireAuth()) return;
|
||||
|
||||
currentUser = await api.getCurrentUser();
|
||||
if (!currentUser) return;
|
||||
|
||||
populateHourSelect();
|
||||
populateForm();
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
function populateHourSelect() {
|
||||
const select = document.getElementById('notifyDailyHour');
|
||||
for (let h = 0; h < 24; h++) {
|
||||
const option = document.createElement('option');
|
||||
option.value = h;
|
||||
option.textContent = h.toString().padStart(2, '0');
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
function populateForm() {
|
||||
document.getElementById('weekStartDay').value = currentUser.week_start_day || 0;
|
||||
|
||||
// Notification settings
|
||||
document.getElementById('notifyWeeklyParking').checked = currentUser.notify_weekly_parking !== 0;
|
||||
document.getElementById('notifyDailyParking').checked = currentUser.notify_daily_parking !== 0;
|
||||
document.getElementById('notifyDailyHour').value = currentUser.notify_daily_parking_hour || 8;
|
||||
document.getElementById('notifyDailyMinute').value = currentUser.notify_daily_parking_minute || 0;
|
||||
document.getElementById('notifyParkingChanges').checked = currentUser.notify_parking_changes !== 0;
|
||||
|
||||
updateDailyTimeVisibility();
|
||||
}
|
||||
|
||||
function updateDailyTimeVisibility() {
|
||||
const enabled = document.getElementById('notifyDailyParking').checked;
|
||||
document.getElementById('dailyTimeGroup').style.display = enabled ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// Settings form
|
||||
document.getElementById('settingsForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const data = {
|
||||
week_start_day: parseInt(document.getElementById('weekStartDay').value)
|
||||
};
|
||||
|
||||
const response = await api.put('/api/users/me/settings', data);
|
||||
if (response && response.ok) {
|
||||
utils.showMessage('Settings saved successfully', 'success');
|
||||
currentUser = await api.getCurrentUser();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
utils.showMessage(error.detail || 'Failed to save settings', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
// Notification form
|
||||
document.getElementById('notificationForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const data = {
|
||||
notify_weekly_parking: document.getElementById('notifyWeeklyParking').checked ? 1 : 0,
|
||||
notify_daily_parking: document.getElementById('notifyDailyParking').checked ? 1 : 0,
|
||||
notify_daily_parking_hour: parseInt(document.getElementById('notifyDailyHour').value),
|
||||
notify_daily_parking_minute: parseInt(document.getElementById('notifyDailyMinute').value),
|
||||
notify_parking_changes: document.getElementById('notifyParkingChanges').checked ? 1 : 0
|
||||
};
|
||||
|
||||
const response = await api.put('/api/users/me/settings', data);
|
||||
if (response && response.ok) {
|
||||
utils.showMessage('Notification settings saved', 'success');
|
||||
currentUser = await api.getCurrentUser();
|
||||
} else {
|
||||
const error = await response.json();
|
||||
utils.showMessage(error.detail || 'Failed to save notifications', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle daily time visibility
|
||||
document.getElementById('notifyDailyParking').addEventListener('change', updateDailyTimeVisibility);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user