Compare commits

..

2 Commits

Author SHA1 Message Date
466badd326 Fix avatar upload requiring username/email (Issue #12)
- Remove unnecessary wrapper form around Profile Picture section
- Avatar upload form is now standalone, not nested
- Prevents browser validation from requiring username/email when uploading avatar

Fixes #12
2025-10-11 23:58:01 -05:00
29b4a9d339 Add topbar navigation to all pages and make logo clickable (Issues #14, #13)
- Create reusable _nav.html navigation include
- Add topbar to all settings pages (settings, profile, communities, filters, experience)
- Add topbar to all admin pages (admin, polling, polling_logs, setup)
- Replace hardcoded nav in dashboard with include
- Wrap logo in link to index page (fixes clicking logo to go home)

Fixes #14, #13
2025-10-11 23:56:52 -05:00
10 changed files with 77 additions and 71 deletions

47
templates/_nav.html Normal file
View File

@@ -0,0 +1,47 @@
<!-- Modern Top Navigation -->
<nav class="top-nav">
<div class="nav-content">
<div class="nav-left">
<a href="{{ url_for('index') }}" class="logo-section">
<img src="{{ url_for('serve_logo') }}" alt="BalanceBoard" class="nav-logo">
<span class="brand-text"><span class="brand-balance">balance</span><span class="brand-board">Board</span></span>
</a>
</div>
<div class="nav-center">
<div class="search-bar">
<input type="text" placeholder="Search content..." class="search-input">
<button class="search-btn">🔍</button>
</div>
</div>
<div class="nav-right">
{% if current_user.is_authenticated %}
<div class="user-menu">
<div class="user-info">
<div class="user-avatar">
{% if current_user.profile_picture_url %}
<img src="{{ current_user.profile_picture_url }}" alt="Avatar">
{% else %}
<div class="avatar-placeholder">{{ current_user.username[:2].upper() }}</div>
{% endif %}
</div>
<span class="username">{{ current_user.username }}</span>
</div>
<div class="user-dropdown">
<a href="{{ url_for('settings') }}" class="dropdown-item">⚙️ Settings</a>
{% if current_user.is_admin %}
<a href="{{ url_for('admin_panel') }}" class="dropdown-item">👨‍💼 Admin Panel</a>
{% endif %}
<a href="{{ url_for('logout') }}" class="dropdown-item">🚪 Logout</a>
</div>
</div>
{% else %}
<div class="anonymous-actions">
<a href="{{ url_for('login') }}" class="login-btn">🔑 Login</a>
<a href="{{ url_for('signup') }}" class="register-btn">📝 Sign Up</a>
</div>
{% endif %}
</div>
</div>
</nav>

View File

@@ -345,6 +345,7 @@
</style> </style>
</head> </head>
<body> <body>
{% include '_nav.html' %}
<div class="admin-container"> <div class="admin-container">
<a href="{{ url_for('index') }}" class="back-link">← Back to Feed</a> <a href="{{ url_for('index') }}" class="back-link">← Back to Feed</a>

View File

@@ -177,6 +177,7 @@
</style> </style>
</head> </head>
<body> <body>
{% include '_nav.html' %}
<div class="admin-container"> <div class="admin-container">
<div class="admin-header"> <div class="admin-header">
<h1>📡 Polling Management</h1> <h1>📡 Polling Management</h1>

View File

@@ -107,6 +107,7 @@
</style> </style>
</head> </head>
<body> <body>
{% include '_nav.html' %}
<div class="admin-container"> <div class="admin-container">
<div class="admin-header"> <div class="admin-header">
<h1>📋 Polling Logs</h1> <h1>📋 Polling Logs</h1>

View File

@@ -3,54 +3,7 @@
{% block title %}Dashboard - BalanceBoard{% endblock %} {% block title %}Dashboard - BalanceBoard{% endblock %}
{% block content %} {% block content %}
<!-- Modern Top Navigation --> {% include '_nav.html' %}
<nav class="top-nav">
<div class="nav-content">
<div class="nav-left">
<div class="logo-section">
<img src="{{ url_for('serve_logo') }}" alt="BalanceBoard" class="nav-logo">
<span class="brand-text"><span class="brand-balance">balance</span><span class="brand-board">Board</span></span>
</div>
</div>
<div class="nav-center">
<div class="search-bar">
<input type="text" placeholder="Search content..." class="search-input">
<button class="search-btn">🔍</button>
</div>
</div>
<div class="nav-right">
{% if anonymous %}
<div class="anonymous-actions">
<a href="{{ url_for('login') }}" class="login-btn">🔑 Login</a>
<a href="{{ url_for('signup') }}" class="register-btn">📝 Sign Up</a>
</div>
{% else %}
{# This block only executes for authenticated users (per app.py line 278) #}
<div class="user-menu">
<div class="user-info">
<div class="user-avatar">
{% if current_user.profile_picture_url %}
<img src="{{ current_user.profile_picture_url }}" alt="Avatar">
{% else %}
<div class="avatar-placeholder">{{ current_user.username[:2].upper() }}</div>
{% endif %}
</div>
<span class="username">{{ current_user.username }}</span>
</div>
<div class="user-dropdown">
<a href="{{ url_for('settings') }}" class="dropdown-item">⚙️ Settings</a>
{% if current_user.is_admin %}
<a href="{{ url_for('admin_panel') }}" class="dropdown-item">👨‍💼 Admin Panel</a>
{% endif %}
<a href="{{ url_for('logout') }}" class="dropdown-item">🚪 Logout</a>
</div>
</div>
{% endif %}
</div>
</div>
</nav>
<!-- Main Content Area --> <!-- Main Content Area -->
<main class="main-content"> <main class="main-content">

View File

@@ -230,6 +230,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include '_nav.html' %}
<div class="settings-container"> <div class="settings-container">
<div class="settings-header"> <div class="settings-header">
<h1>Settings</h1> <h1>Settings</h1>

View File

@@ -235,6 +235,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include '_nav.html' %}
<div class="settings-container"> <div class="settings-container">
<div class="settings-header"> <div class="settings-header">
<h1>Community Settings</h1> <h1>Community Settings</h1>

View File

@@ -241,6 +241,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include '_nav.html' %}
<div class="experience-settings"> <div class="experience-settings">
<div class="experience-header"> <div class="experience-header">
<h1>Experience Settings</h1> <h1>Experience Settings</h1>

View File

@@ -263,6 +263,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include '_nav.html' %}
<div class="settings-container"> <div class="settings-container">
<div class="settings-header"> <div class="settings-header">
<h1>Filter Settings</h1> <h1>Filter Settings</h1>

View File

@@ -225,6 +225,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include '_nav.html' %}
<div class="settings-container"> <div class="settings-container">
<div class="settings-header"> <div class="settings-header">
<h1>Profile Settings</h1> <h1>Profile Settings</h1>
@@ -242,31 +243,29 @@
{% endwith %} {% endwith %}
</div> </div>
<form method="POST"> <div class="profile-section">
<div class="profile-section"> <h2>Profile Picture</h2>
<h2>Profile Picture</h2> <div class="profile-avatar">
<div class="profile-avatar"> <div class="avatar-preview">
<div class="avatar-preview"> {% if user.profile_picture_url %}
{% if user.profile_picture_url %} <img src="{{ user.profile_picture_url }}" alt="{{ user.username }}">
<img src="{{ user.profile_picture_url }}" alt="{{ user.username }}"> {% else %}
{% else %} {{ user.username[0]|upper }}
{{ user.username[0]|upper }} {% endif %}
{% endif %} </div>
</div> <div class="avatar-info">
<div class="avatar-info"> <h3>Current Avatar</h3>
<h3>Current Avatar</h3> <p>Upload a new profile picture to personalize your account</p>
<p>Upload a new profile picture to personalize your account</p> <form id="upload-form" method="POST" action="{{ url_for('upload_avatar') }}" enctype="multipart/form-data">
<form id="upload-form" method="POST" action="{{ url_for('upload_avatar') }}" enctype="multipart/form-data"> <div class="file-upload">
<div class="file-upload"> <input type="file" id="avatar" name="avatar" accept="image/*" onchange="this.form.submit()">
<input type="file" id="avatar" name="avatar" accept="image/*" onchange="this.form.submit()"> <label for="avatar" class="file-upload-label">Choose New Picture</label>
<label for="avatar" class="file-upload-label">Choose New Picture</label> </div>
</div> <p class="help-text">PNG, JPG, or GIF. Maximum size 2MB.</p>
<p class="help-text">PNG, JPG, or GIF. Maximum size 2MB.</p> </form>
</form>
</div>
</div> </div>
</div> </div>
</form> </div>
<form method="POST"> <form method="POST">
<div class="profile-section"> <div class="profile-section">