feat: validate user availability

This commit is contained in:
Mann Patel
2025-09-09 10:42:24 -06:00
parent 287068becf
commit 144436bbf3
13 changed files with 544 additions and 704 deletions

View File

@@ -36,7 +36,7 @@
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
font-size: 14px;
}
/* Mobile sidebar overlay */
.sidebar-overlay {
display: none;
@@ -106,7 +106,7 @@
{{ if .IsAuthenticated }}
<!-- Authenticated User Interface -->
<div class="min-h-screen">
<!-- Mobile sidebar overlay -->
<div id="sidebar-overlay" class="sidebar-overlay" onclick="toggleSidebar()"></div>
@@ -167,11 +167,6 @@
</a>
{{ end }}
<a href="/profile" class="flex items-center px-3 py-2.5 text-sm {{if eq .ActiveSection "profile"}}bg-blue-light text-blue-primary border-r-4 border-blue-primary pl-2 rounded-none{{else}}text-text-secondary hover:bg-gray-50 rounded-md{{end}} group">
<i class="fas fa-user-circle w-5 {{if eq .ActiveSection "profile"}}text-blue-primary{{else}}text-gray-400{{end}} mr-3"></i>
<span {{if eq .ActiveSection "profile"}}class="font-medium"{{end}}>Profile</span>
</a>
<a href="/logout" class="flex items-center px-3 py-2.5 text-sm text-text-secondary hover:bg-gray-50 rounded-md group">
<i class="fas fa-sign-out-alt w-5 text-gray-400 mr-3"></i>
<span>Logout</span>
@@ -183,7 +178,7 @@
<!-- Main Content Container -->
<div class="main-content-container min-h-screen flex flex-col bg-custom-gray">
<!-- Top Header -->
<div class="bg-white border-b border-border-gray px-4 md:px-6 py-4">
<div class="fixed top-0 left-0 right-0 z-20 bg-white border-b border-border-gray px-4 md:px-6 py-4">
<div class="flex items-center justify-between">
<!-- Hamburger (left aligned with consistent spacing) -->
<div class="flex items-center">
@@ -195,9 +190,9 @@
<!-- Right side -->
<div class="flex items-center space-x-2 md:space-x-4">
<!-- Dark mode -->
<button class="text-text-secondary hover:text-text-primary p-2">
<i class="fas fa-moon text-lg"></i>
</button>
<a href="/logout" class="text-text-secondary hover:text-text-primary p-2">
<i class="fa-solid fa-arrow-right-from-bracket text-lg"></i>
</a>
<!-- Profile (hover dropdown on desktop, click on mobile) -->
<div class="relative group cursor-pointer">
@@ -211,7 +206,7 @@
<!-- Dropdown -->
<div id="profile-menu" class="absolute right-0 mt-2 w-40 bg-white border border-border-gray rounded-md shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
<a href="/profile" class="block px-4 py-2 text-sm text-text-primary hover:bg-gray-100">Profile</a>
<a href="#" class="block px-4 py-2 text-sm text-text-primary hover:bg-gray-100">Settings</a>
<a href="/profile" class="block px-4 py-2 text-sm text-text-primary hover:bg-gray-100">Settings</a>
<a href="/logout" class="block px-4 py-2 text-sm text-red-600 hover:bg-gray-100">Logout</a>
</div>
</div>
@@ -220,7 +215,7 @@
</div>
<!-- Page Content -->
<div class="flex-1">
<div class="flex-1 mt-20">
{{ template "content" . }}
</div>
</div>
@@ -229,15 +224,15 @@
{{else}}
<!-- Split Screen Login/Register Page -->
<div class="min-h-screen flex" x-data="{ isLogin: true }">
<!-- Left Side - Image -->
<div class="hidden lg:flex flex-1 relative overflow-hidden">
<!-- Background overlay for better text readability -->
<div class="absolute inset-0 bg-gradient-to-br from-blue-500/20 to-blue-700/40 z-10"></div>
<!-- Background Image -->
<img src="../../static/feature-mobile1.jpg" alt="Welcome to Poll System" class="object-cover w-full h-full"/>
<!-- Logo and branding overlay -->
<div class="absolute top-8 left-8 z-20 flex items-center gap-3">
<div class="w-10 h-10 bg-white/20 backdrop-blur-sm rounded-full flex items-center justify-center">
@@ -245,7 +240,7 @@
</div>
<span class="text-2xl font-bold text-white">Linq</span>
</div>
<!-- Welcome text overlay -->
<div class="absolute bottom-8 left-8 right-8 z-20 text-white">
<h1 class="text-4xl font-bold mb-4">Welcome to Poll System</h1>
@@ -256,7 +251,7 @@
<!-- Right Side - Login/Register Forms -->
<div class="flex-1 flex items-center justify-center p-6 lg:p-12 bg-white">
<div class="w-full max-w-md">
<!-- Mobile Logo (visible only on small screens) -->
<div class="lg:hidden flex items-center justify-center gap-3 mb-8">
<div class="w-10 h-10 bg-blue-primary rounded-full flex items-center justify-center">
@@ -282,27 +277,27 @@
<h2 class="text-3xl font-bold text-text-primary">Welcome back</h2>
<p class="text-text-secondary mt-2">Please sign in to your account</p>
</div>
<div>
<label for="login_email" class="block text-sm font-medium text-text-primary mb-2">Email Address</label>
<input type="email"
id="login_email"
name="email"
required
placeholder="Enter your email"
<input type="email"
id="login_email"
name="email"
required
placeholder="Enter your email"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
<div>
<label for="login_password" class="block text-sm font-medium text-text-primary mb-2">Password</label>
<input type="password"
id="login_password"
name="password"
required
placeholder="Enter your password"
<input type="password"
id="login_password"
name="password"
required
placeholder="Enter your password"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
<div class="flex items-center justify-between">
<label class="flex items-center">
<input type="checkbox" class="h-4 w-4 text-blue-primary focus:ring-blue-primary border-gray-300 rounded">
@@ -310,7 +305,7 @@
</label>
<a href="#" class="text-sm text-blue-primary hover:text-blue-600">Forgot password?</a>
</div>
<button type="submit" class="w-full bg-blue-primary text-white py-3 rounded-lg hover:bg-blue-600 focus:ring-2 focus:ring-blue-primary focus:ring-offset-2 transition-colors font-medium">
Sign In
</button>
@@ -324,49 +319,49 @@
<h2 class="text-3xl font-bold text-text-primary">Create Account</h2>
<p class="text-text-secondary mt-2">Join our polling platform today</p>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-text-primary mb-2">First Name</label>
<input type="text"
name="first_name"
required
placeholder="First Name"
<input type="text"
name="first_name"
required
placeholder="First Name"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
<div>
<label class="block text-sm font-medium text-text-primary mb-2">Last Name</label>
<input type="text"
name="last_name"
required
placeholder="Last Name"
<input type="text"
name="last_name"
required
placeholder="Last Name"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
</div>
<div>
<label class="block text-sm font-medium text-text-primary mb-2">Email Address</label>
<input type="email"
name="email"
required
placeholder="Enter your email"
<input type="email"
name="email"
required
placeholder="Enter your email"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
<div>
<label class="block text-sm font-medium text-text-primary mb-2">Phone</label>
<input type="tel"
<input type="tel"
name="phone"
required
placeholder="Phone number"
placeholder="Phone number"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
<div>
<label class="block text-sm font-medium text-text-primary mb-2">Role</label>
<select name="role"
required
onchange="toggleAdminCodeField()"
<select name="role"
required
onchange="toggleAdminCodeField()"
id="role"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors">
<option value="">Select Role</option>
@@ -374,26 +369,26 @@
<option value="3">Volunteer</option>
</select>
</div>
<!-- Admin/Team Leader Code Field (hidden by default) -->
<div id="adminCodeField" class="hidden">
<label class="block text-sm font-medium text-text-primary mb-2">Access Code</label>
<input type="password"
name="admin_code"
placeholder="Enter access code"
<input type="password"
name="admin_code"
placeholder="Enter access code"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
<p class="text-xs text-text-secondary mt-1">Required for Admin and Team Leader roles</p>
</div>
<div>
<label class="block text-sm font-medium text-text-primary mb-2">Password</label>
<input type="password"
name="password"
required
placeholder="Create a password"
<input type="password"
name="password"
required
placeholder="Create a password"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-primary focus:border-transparent transition-colors"/>
</div>
<button type="submit" class="w-full bg-blue-primary text-white py-3 rounded-lg hover:bg-blue-600 focus:ring-2 focus:ring-blue-primary focus:ring-offset-2 transition-colors font-medium">
Create Account
</button>
@@ -410,21 +405,42 @@
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
if (sidebar && sidebarOverlay) {
sidebar.classList.toggle('active');
sidebarOverlay.classList.toggle('active');
}
}
// Profile menu toggle
function toggleProfileMenu() {
const menu = document.getElementById('profile-menu');
if (menu) {
menu.classList.toggle('opacity-0');
menu.classList.toggle('invisible');
menu.classList.toggle('opacity-100');
menu.classList.toggle('visible');
const isVisible = !menu.classList.contains('invisible');
if (isVisible) {
// Hide it
menu.classList.add('opacity-0', 'invisible');
menu.classList.remove('opacity-100', 'visible');
document.removeEventListener('click', outsideClickListener);
} else {
// Show it
menu.classList.remove('opacity-0', 'invisible');
menu.classList.add('opacity-100', 'visible');
setTimeout(() => {
document.addEventListener('click', outsideClickListener);
}, 0);
}
}
}
function outsideClickListener(event) {
const menu = document.getElementById('profile-menu');
const trigger = event.target.closest('.group'); // The profile button wrapper
if (menu && !menu.contains(event.target) && !trigger) {
// Hide menu
menu.classList.add('opacity-0', 'invisible');
menu.classList.remove('opacity-100', 'visible');
document.removeEventListener('click', outsideClickListener);
}
}
@@ -432,7 +448,7 @@
function toggleAdminCodeField() {
const role = document.getElementById("role");
const field = document.getElementById("adminCodeField");
if (role && field) {
const roleValue = role.value;
if (roleValue === "1") { // Admin or Team Leader
@@ -449,7 +465,7 @@
const sidebar = document.getElementById('sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
const body = document.body;
if (sidebar && sidebarOverlay && sidebar.classList.contains('active')) {
sidebar.classList.remove('active');
sidebarOverlay.classList.remove('active');
@@ -464,7 +480,7 @@
const sidebar = document.getElementById('sidebar');
const sidebarOverlay = document.getElementById('sidebar-overlay');
const body = document.body;
if (sidebar && sidebarOverlay) {
sidebar.classList.remove('active');
sidebarOverlay.classList.remove('active');
@@ -485,4 +501,4 @@
</script>
</body>
</html>
{{end}}
{{end}}