Skip to content

Added profile page, avatar handling, navbar synchronization and tests#65

Open
ghanshyam2005singh wants to merge 2 commits into
alphaonelabs:mainfrom
ghanshyam2005singh:feat/week2-user-profile-directory
Open

Added profile page, avatar handling, navbar synchronization and tests#65
ghanshyam2005singh wants to merge 2 commits into
alphaonelabs:mainfrom
ghanshyam2005singh:feat/week2-user-profile-directory

Conversation

@ghanshyam2005singh

@ghanshyam2005singh ghanshyam2005singh commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR added the profile system, avatar, user info and expanding profile test coverage.

Changes

Profile page

  • Edit bio, set username of socials and delete account
  • Can set public or private profile

Navbar Profile Updates

  • Fixed navbar loading inconsistencies.
  • Navbar now updates immediately when profile information changes.

Avatar Storage

  • Added a fallback avatar storage mechanism.
  • When an R2 binding is available, avatar uploads continue to use R2 object storage.
  • When R2 is unavailable or not configured, avatars are stored as Base64 data URLs to preserve development and local testing functionality.
  • Added documentation and configuration guidance for creating the required learn-avatars R2 bucket.

Testing

  • Added and updated profile-related tests.
  • Expanded coverage for avatar upload and profile functionality.
  • Verified behavior for both R2-backed and fallback avatar storage paths.

Notes

To enable production avatar storage:

  1. Enable R2 in the Cloudflare dashboard.
  2. Create the avatar bucket:
wrangler r2 bucket create learn-avatars
  1. Uncomment the R2 binding block in wrangler.toml.
  2. Configure R2_PUBLIC_URL.

The Base64 fallback remains available for local development and environments where R2 is not configured.

Summary

This PR introduces a comprehensive user profile system, enabling users to manage their profiles, upload avatars, and discover other community members.

Key Features & Changes

Profile Management

  • New profile editing page (profile.html) allowing users to update bio, set social media usernames (GitHub, Discord, Slack), manage expertise tags, toggle teacher status and profile visibility, and delete their account
  • Public profile viewing page (public-profile.html) displaying user information when profiles are set to public
  • New member directory (users.html) with search and "Teachers Only" filter for discovering community members

Navbar & Authentication

  • Updated navbar to show authenticated user's avatar/initials, name, and role with expanded profile dropdown menu containing links to dashboard, edit profile, avatar management, and public profile
  • Enhanced navbar injection mechanism to prefetch and inject navbar/footer HTML concurrently, reducing load times
  • Immediate synchronization of profile changes (avatar, name) to the navbar without page reload

Avatar Handling

  • Dual-storage approach: uploads to Cloudflare R2 object storage when configured, with fallback to Base64 data URLs for local development and unsupported environments
  • Avatar upload with validation (file type, size limits) and client-side image resizing
  • Avatar removal capability with UI state reset
  • Avatar history tracking in database

Backend API

  • New endpoints for profile management: GET /api/profile, PATCH /api/profile, DELETE /api/account
  • Avatar endpoints: POST /api/profile/avatar, DELETE /api/profile/avatar
  • Public user discovery: GET /api/users, GET /api/users/:username
  • Password-protected account deletion with cascading cleanup of user-owned content

Database Schema

  • Extended users table with profile fields: bio, avatar_url, social usernames, expertise, teaching flag, and profile visibility toggle
  • New avatars table tracking avatar history per user
  • Optimized indexing for public profile queries

Testing

  • Comprehensive test suite (test_api_profile.py) covering profile CRUD operations, avatar uploads, public directory filtering, and access control
  • Updated existing tests to reflect schema changes

Technical Notes

R2 bucket configuration is currently commented out in wrangler.toml; enabling production avatar storage requires creating the learn-avatars bucket and configuring the R2_PUBLIC_URL environment variable.

User Experience Impact

  • Users now have full control over their public profile visibility and presentation
  • Community members can discover and connect via the member directory
  • Real-time navbar updates reflect profile changes without page reloads
  • Seamless avatar uploads with automatic fallback support for all environments

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ghanshyam2005singh, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 38 minutes and 58 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dee9f311-a41e-47d5-93b3-7c8a4e30f958

📥 Commits

Reviewing files that changed from the base of the PR and between e53baf8 and 952bde6.

📒 Files selected for processing (7)
  • public/js/layout.js
  • public/partials/navbar.html
  • public/profile.html
  • public/public-profile.html
  • public/users.html
  • tests/test_api_profile.py
  • wrangler.toml

Walkthrough

This PR introduces a complete user profile system: users can edit their profile (name, bio, expertise, avatar, social links, visibility), view others' public profiles, and discover members through a searchable directory. The backend adds profile/avatar/account API endpoints with public discovery gating, the database schema expands to store profile fields and avatar history, and the frontend adds a profile editor, public profile viewer, member directory, and enhanced navigation auth UI with avatar display.

Changes

User Profiles & Public Discovery

Layer / File(s) Summary
Database schema and migrations
schema.sql, src/worker.py
users table gains profile fields (bio, avatar_url, is_teacher, social usernames, expertise, is_profile_public). New avatars table tracks avatar history per user with cascading deletes. Idempotent migration logic safely adds columns to existing databases. Schema mockups in tests updated to reflect new migration sequence.
Profile fetch and update endpoints
src/worker.py, tests/test_api_profile.py
GET /api/profile returns authenticated user's decrypted full profile; PATCH /api/profile updates editable fields with encryption and boolean normalization. Tests validate auth, field exposure, omission of sensitive data, and update coercion.
Avatar upload and removal
src/worker.py, tests/test_api_profile.py
POST /api/profile/avatar validates image type/size, resizes via canvas, stores as base64 data-URL, updates user record, and records avatar history. DELETE /api/profile/avatar removes avatar and reverts UI. Tests verify constraints, auth, and base64 response.
Account deletion and public discovery
src/worker.py, tests/test_api_profile.py
DELETE /api/account deletes user after password verification with dependent activity cleanup. GET /api/users lists public profiles with bio excerpt limiting. GET /api/users/:username retrieves public profile gated by visibility flag, distinguishing 403/private from 404/missing. Tests validate auth, visibility gating, and field exposure.
API routing and login integration
src/worker.py
New routes wire profile/avatar/account/discovery endpoints. Login response expanded to include avatar_url.
Navigation bar avatar and auth UI
public/js/layout.js, public/partials/navbar.html
inject() refactored to accept fetch promises for concurrent navbar/footer loading. Avatar helpers _setNavAvatar and _setNavButtonAvatar switch between initials and images with error fallback. updateAuthSection() computes firstName/avatarUrl/role and updates desktop/mobile nav with greetings, usernames, role text, avatar visuals, and "My Public Profile" links. Navbar expanded with multi-section desktop dropdown and styled mobile auth card.
User profile editor page
public/profile.html
Two-column authenticated editor: left column for avatar upload/removal, display name, bio (with live counter), expertise, social links (GitHub/Discord/Slack), teacher/public toggles, and account deletion modal; right column for stats, enrollments, badges. Client-side enforces auth via localStorage, loads profile/dashboard data, provides saveProfile() to PATCH changes, handles avatar validation/resize/upload with optimistic preview, implements removeAvatar() for deletion, and confirmDelete() for account termination.
Public profile viewer page
public/public-profile.html
Single-user read-only profile display. Fetches profile by username from URL query, handles 404/403 authorization states, renders title/name/username/bio/expertise/social/activities with teacher badge, avatar image or initials fallback, and activities with type-based icons and navigation links. Conditionally hides empty sections.
Member directory and search
public/users.html
Searchable member directory with hero, search input, and "Teachers Only" toggle. Fetches /api/users on load, renders responsive card grid with avatar/teacher badge/expertise chips/bio snippet and profile links. Filters in-memory allUsers by name/username/expertise/bio and optional teacher flag. Shows loading skeleton, empty state on no results, and result count.
Configuration documentation
wrangler.toml
Commented R2 bucket setup instructions (learn-avatars) for optional cloud avatar storage. Implementation uses data-URL fallback when unconfigured.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • alphaonelabs/learn#61: Modifies shared public/js/layout.js entry points (inject, initLayout, updateAuthSection) in the same auth UI and navbar injection flow—this PR expands the avatar/greeting/profile-link logic that PR establishes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.93% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Added profile page, avatar handling, navbar synchronization and tests' accurately summarizes the main changes across multiple file categories and reflects the PR's core objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
public/partials/navbar.html (2)

11-13: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add aria-label to mobile menu button for screen readers.

The mobile menu toggle button should have an aria-label attribute to describe its purpose for assistive technology users.

♿ Proposed accessibility improvement
-<button class="md:hidden p-2 hover:bg-teal-700 rounded-lg" onclick="toggleMobileMenu()">
+<button class="md:hidden p-2 hover:bg-teal-700 rounded-lg" onclick="toggleMobileMenu()" aria-label="Toggle mobile menu">
     <i class="fas fa-bars text-xl"></i>
 </button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/partials/navbar.html` around lines 11 - 13, The mobile menu toggle
button lacks an accessible name; add an aria-label attribute to the button
element that calls toggleMobileMenu() (e.g., aria-label="Toggle main menu" or
"Open main menu") so screen readers can describe its purpose; ensure the
attribute is placed on the <button> that currently has class "md:hidden p-2
hover:bg-teal-700 rounded-lg" and consider updating the label dynamically inside
the toggleMobileMenu() handler if you change state (open/close).

224-232: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add ARIA attributes to profile dropdown button.

The profile dropdown button should include aria-haspopup="true" and aria-expanded to communicate its interactive state to screen readers. Additionally, the button and dropdown menu should be associated with proper ARIA roles.

♿ Proposed accessibility improvement
-<button onclick="toggleProfileDropdown()" class="w-full flex items-center space-x-2 p-2 hover:bg-teal-700 rounded-lg transition">
+<button onclick="toggleProfileDropdown()" class="w-full flex items-center space-x-2 p-2 hover:bg-teal-700 rounded-lg transition" aria-haspopup="true" aria-expanded="false" aria-controls="profile-dropdown">
     <!-- Profile Avatar with First Letter -->
     <div id="profile-avatar" class="w-8 h-8 rounded-full bg-orange-500 flex items-center justify-center font-bold text-white text-sm">

Then update the toggleProfileDropdown() function in layout.js to toggle the aria-expanded attribute:

window.toggleProfileDropdown = function () {
    const dropdown = document.getElementById('profile-dropdown');
    const button = document.querySelector('[aria-controls="profile-dropdown"]');
    if (dropdown) {
        const isHidden = dropdown.classList.toggle('hidden');
        if (button) button.setAttribute('aria-expanded', !isHidden);
    }
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/partials/navbar.html` around lines 224 - 232, Add proper ARIA
attributes to the profile dropdown button and associate it with the dropdown:
update the button markup (the element that calls toggleProfileDropdown()) to
include aria-haspopup="true", aria-expanded="false", and
aria-controls="profile-dropdown" so screen readers can discover the controlled
element; add role="menu" to the element with id="profile-dropdown" and
role="menuitem" to each dropdown entry. Then update the toggleProfileDropdown()
function to find the button via
document.querySelector('[aria-controls="profile-dropdown"]') and toggle its
aria-expanded value whenever it shows/hides the element with id
"profile-dropdown" (use the existing dropdown.classList.toggle('hidden') result
to set aria-expanded to the inverse of the hidden state).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@public/js/layout.js`:
- Around line 104-113: The _setNavButtonAvatar function injects unescaped
user-derived initials into an inline onerror attribute, creating an XSS risk;
remove the inline event handler and instead create the IMG element via DOM APIs
(document.createElement('img')), set its src/class/alt normally, and attach a
safe onerror handler (img.onerror = () => { el.textContent = initials; }) or set
el.textContent using a properly escaped value before/after insertion; ensure you
only use element.textContent (not innerHTML) to render initials and reference
_setNavButtonAvatar and the avatarElId/initials variables when updating the
implementation.

In `@public/partials/navbar.html`:
- Around line 235-282: The logout button inside the profile dropdown (element id
"profile-dropdown") currently uses <button onclick="logout()">; add
type="button" to that button to prevent accidental form submission (i.e., change
the button with onclick="logout()" to include type="button"); also scan the
dropdown for any other plain <button> elements and add explicit type attributes
as needed to be safe.
- Around line 330-368: The logout button lacks an explicit type which can cause
unintended form submissions; update the button element that calls logout() (the
element with onclick="logout()") to include type="button" so it won't submit any
parent form — locate the button with the logout() onclick in the mobile auth
panel (currently inside the `#mobile-auth-logged-in` block) and add the
type="button" attribute.

In `@public/profile.html`:
- Around line 80-84: Add an explicit type="button" attribute to every button
that is not intended to submit a form to prevent accidental form submissions:
update the Upload Photo button that triggers
document.getElementById('avatar-file-input').click(), the button with id
"btn-remove-avatar" (onclick removeAvatar()), and likewise add type="button" to
the toggle switch buttons, the profile Save button, the Delete Account button,
and both modal buttons (confirm/cancel) so none default to type="submit".

In `@public/public-profile.html`:
- Around line 190-198: Add an onerror fallback to the avatar image element so a
broken avatar URL falls back to initials: when setting
document.getElementById('profile-avatar-img').src from p.avatar_url, also attach
an onerror handler that hides the image, shows
document.getElementById('profile-initials'), and sets its textContent to the
computed initials (same logic as the else branch); ensure the handler is
idempotent (removes itself or checks to avoid loops) and uses the existing
initials computation based on p.name || p.username || '?' so the page matches
the users.html behavior.

In `@public/users.html`:
- Around line 51-63: Add accessible labeling and explicit button type: associate
the search input (id="search-input", used by filterUsers()) with an accessible
label (either add a visually-hidden <label for="search-input"> or set aria-label
on the input) so screen readers announce its purpose, and set the filter button
(id="filter-teachers", used by toggleTeacherFilter()) to type="button" to
prevent implicit form submission; ensure you use the existing sr-only utility
(or Tailwind's sr-only) if adding a hidden label.

In `@tests/test_api_profile.py`:
- Around line 359-407: Add a new test in the TestUploadAvatar class that
exercises the R2-enabled path by providing a mocked R2 binding on the env and
asserting the returned avatar_url is an R2 URL (instead of the data: URL); reuse
the helpers used in this file (make_env, MockDB, make_stmt, _req,
api_upload_avatar) to construct env with db=MockDB([...]) and set env.R2 to a
mock object that implements the expected put/upload behavior, call
worker.api_upload_avatar with a small base64 PNG payload (like in
test_no_r2_falls_back_to_base64), assert r.status == 200, and assert
_parse(r)["data"]["avatar_url"] matches the expected R2 URL pattern (or contains
the mock R2 key returned by your mock).

In `@wrangler.toml`:
- Around line 22-26: Update the wrangler.toml R2 bucket comment block to include
documentation for the required R2_PUBLIC_URL environment variable so avatar URLs
resolve in production; explicitly instruct users to create the bucket (wrangler
r2 bucket create learn-avatars), then set R2_PUBLIC_URL in their Cloudflare
Workers environment (Workers → Settings → Variables) to the bucket’s public URL
(e.g., https://pub-xxxxx.r2.dev), and keep the existing [[r2_buckets]] example
(binding = "R2", bucket_name = "learn-avatars"); reference R2_PUBLIC_URL and the
avatar URL logic in worker.py (around lines ~2750-2751) so maintainers see why
the variable is needed.

---

Outside diff comments:
In `@public/partials/navbar.html`:
- Around line 11-13: The mobile menu toggle button lacks an accessible name; add
an aria-label attribute to the button element that calls toggleMobileMenu()
(e.g., aria-label="Toggle main menu" or "Open main menu") so screen readers can
describe its purpose; ensure the attribute is placed on the <button> that
currently has class "md:hidden p-2 hover:bg-teal-700 rounded-lg" and consider
updating the label dynamically inside the toggleMobileMenu() handler if you
change state (open/close).
- Around line 224-232: Add proper ARIA attributes to the profile dropdown button
and associate it with the dropdown: update the button markup (the element that
calls toggleProfileDropdown()) to include aria-haspopup="true",
aria-expanded="false", and aria-controls="profile-dropdown" so screen readers
can discover the controlled element; add role="menu" to the element with
id="profile-dropdown" and role="menuitem" to each dropdown entry. Then update
the toggleProfileDropdown() function to find the button via
document.querySelector('[aria-controls="profile-dropdown"]') and toggle its
aria-expanded value whenever it shows/hides the element with id
"profile-dropdown" (use the existing dropdown.classList.toggle('hidden') result
to set aria-expanded to the inverse of the hidden state).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: alphaonelabs/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b659b84-2209-46d9-9e33-968c6e34af85

📥 Commits

Reviewing files that changed from the base of the PR and between 997b01e and e53baf8.

⛔ Files ignored due to path filters (1)
  • migrations/0004_add_user_profiles.sql is excluded by !**/migrations/**
📒 Files selected for processing (12)
  • public/js/layout.js
  • public/partials/navbar.html
  • public/profile.html
  • public/public-profile.html
  • public/users.html
  • schema.sql
  • src/worker.py
  • tests/test_api_activities.py
  • tests/test_api_admin.py
  • tests/test_api_auth.py
  • tests/test_api_profile.py
  • wrangler.toml

Comment thread public/js/layout.js
Comment thread public/partials/navbar.html
Comment thread public/partials/navbar.html
Comment thread public/profile.html Outdated
Comment thread public/public-profile.html
Comment thread public/users.html
Comment thread tests/test_api_profile.py
Comment thread wrangler.toml Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant