Skip to content

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

Open
ghanshyam2005singh wants to merge 4 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 4 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 adds a user profile system with editable public/private profiles, avatar upload/removal, and community discovery—plus navbar synchronization fixes to ensure the logged-in user identity updates immediately and consistently.

Key features & changes

  • Profile editing UI (public/profile.html)

    • Auth-protected profile editor backed by GET /api/profile and PATCH /api/profile
    • Social username fields (GitHub/Discord/Slack), expertise tags, bio with live character counter
    • Teacher/public-profile toggles that drive a generated public profile link
    • Avatar management: validate type/size, resize client-side (max 256px), upload via POST /api/profile/avatar, preview optimistically, and remove via DELETE /api/profile/avatar
    • Account deletion flow via DELETE /api/profile with confirmation validation
  • Public profile page (public/public-profile.html)

    • Client-rendered profile fetched by username from /api/users/:username
    • Handles private profiles (HTTP 403) as “private/not found”, and shows avatar with initials fallback
  • Member directory (public/users.html)

    • Fetches /api/users and renders a searchable grid of public members only
    • Includes a “Teachers Only” client-side filter
  • Navbar synchronization and faster partial loading (public/js/layout.js, public/partials/navbar.html)

    • Prefetches and injects navbar/footer concurrently (Promise-based injection)
    • Updates desktop + mobile navbar identity (name/role, avatar/initials) immediately after profile changes (via window.updateAuthSection() when available)
    • Adds robust avatar image error fallback to initials

Backend API (src/worker.py)

  • Extends authentication output to include avatar_url

  • Adds profile/user-directory endpoints:

    • /api/profile/avatar (upload + remove)
    • /api/profile (existing profile operations implied by UI usage)
    • /api/users and /api/users/:username for public discovery/public profile rendering (respecting is_profile_public)
  • Avatar upload storage supports:

    • Cloudflare R2 when configured (stores avatar_url / avatar_r2_key)
    • Base64 data URL fallback when R2 isn’t available/configured
  • Routing updated for /users and /public-profile, and worker-first handling updated for additional paths in wrangler.toml

Testing

  • Adds a dedicated tests/test_api_profile.py suite covering:
    • Authenticated profile GET/PATCH, account deletion, avatar upload/remove
    • Public directory listing and public profile access control
    • Both avatar storage modes (R2 present vs fallback)
  • Updates existing tests to reflect schema/init sequencing changes and the new avatar_url behavior in login.

User experience impact

  • Users can manage their profile and public visibility, upload an avatar with reliable fallbacks, and discover others via a member directory.
  • Navbar identity updates immediately after profile/avater changes, improving consistency without requiring page reloads.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d37f87fb-51e0-4bdb-b426-d2a1f834a58e

📥 Commits

Reviewing files that changed from the base of the PR and between 10675db and 7067b72.

📒 Files selected for processing (5)
  • public/js/layout.js
  • public/profile.html
  • public/public-profile.html
  • public/users.html
  • src/worker.py

Walkthrough

This PR adds authenticated profile editing, avatar upload/removal, public profile viewing, and a searchable member directory. Backend routes expose profile and public-user data, while frontend pages and navigation render profile identity, avatars, roles, and visibility-aware links.

Changes

User Profiles & Public Discovery

Layer / File(s) Summary
Profile and avatar API
src/worker.py, tests/test_api_profile.py
Adds avatar-aware login output, avatar upload/removal, public profile listing and retrieval, routing, and endpoint coverage.
Authenticated profile editor
public/profile.html
Adds profile loading and updates, avatar processing, dashboard statistics, public-link controls, local-storage synchronization, and confirmed account deletion.
Public profile and member directory pages
public/public-profile.html, public/users.html
Adds public profile rendering and a searchable, teacher-filterable directory with avatar fallbacks, escaped fields, loading states, and empty states.
Navigation authentication UI
public/js/layout.js, public/partials/navbar.html
Prefetches shared partials, injects them concurrently, and renders desktop/mobile greetings, roles, avatars, and public profile links.
Migration fixtures and deployment configuration
schema.sql, tests/test_api_activities.py, tests/test_api_admin.py, tests/test_api_auth.py, wrangler.toml
Updates migration and login test fixtures, preserves schema definitions, adds worker-first routes, and documents optional R2 avatar storage.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clear, concise, and captures the main additions: profile features, avatar support, navbar sync, and tests.
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.
✨ 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.

@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 Outdated
Comment thread public/partials/navbar.html Outdated
Comment thread public/profile.html Outdated
Comment thread public/public-profile.html Outdated
Comment thread public/users.html Outdated
Comment thread tests/test_api_profile.py Outdated
Comment thread wrangler.toml Outdated
@A1L13N

A1L13N commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

@ghanshyam2005singh plesae fix the conflicts

@ghanshyam2005singh
ghanshyam2005singh force-pushed the feat/week2-user-profile-directory branch from 952bde6 to 10675db Compare July 29, 2026 08:13

@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: 14

Caution

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

⚠️ Outside diff range comments (3)
public/js/layout.js (1)

26-39: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use the CSPRNG in the fallback path too.

The happy path correctly uses crypto.getRandomValues, but the catch fallback drops to Date.now() + Math.random(). That branch fires when localStorage is unavailable (private mode, blocked storage) — a case where window.crypto is still perfectly available. Since this token identifies a guest cart, a predictable value means one visitor could guess another's cart token.

🎲 Proposed fix
+function _randomToken() {
+    const bytes = new Uint8Array(32);
+    window.crypto.getRandomValues(bytes);
+    return 'gct_' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
+}
+
 function getGuestCartToken() {
     try {
         let token = localStorage.getItem('guest_cart_token');
         if (!token) {
-            const bytes = new Uint8Array(32);
-            window.crypto.getRandomValues(bytes);
-            token = 'gct_' + Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
+            token = _randomToken();
             localStorage.setItem('guest_cart_token', token);
         }
         return token;
     } catch (_) {
-        return 'gct_' + String(Date.now()) + Math.random().toString(16).slice(2);
+        try {
+            return _randomToken();
+        } catch (_) {
+            return 'gct_' + String(Date.now()) + Math.random().toString(16).slice(2);
+        }
     }
 }

As per path instructions to review JavaScript for security-sensitive patterns.

🤖 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/js/layout.js` around lines 26 - 39, Update the catch fallback in
getGuestCartToken to generate the token using window.crypto.getRandomValues with
the same cryptographically secure byte approach as the primary path, rather than
Date.now() and Math.random(). Preserve the existing gct_ prefix and return
behavior when storage is unavailable.

Sources: Path instructions, Linters/SAST tools

public/partials/navbar.html (1)

54-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expose the dropdown's open/closed state to assistive tech.

The trigger has a helpful aria-label, but screen-reader users get no signal that it controls a collapsible menu or whether it's currently open. Adding aria-haspopup, aria-expanded and aria-controls (with toggleProfileDropdown() keeping aria-expanded in sync) makes the menu announce correctly.

♿ Proposed fix
-          <button onclick="toggleProfileDropdown()" class="flex items-center space-x-2 hover:bg-teal-700 rounded-lg p-2 min-h-11 w-full" aria-label="Open profile menu">
+          <button type="button" onclick="toggleProfileDropdown()" class="flex items-center space-x-2 hover:bg-teal-700 rounded-lg p-2 min-h-11 w-full"
+                  aria-label="Open profile menu" aria-haspopup="true" aria-expanded="false" aria-controls="profile-dropdown">

Then in the toggle handler, mirror the state:

btn.setAttribute('aria-expanded', String(!dropdown.classList.contains('hidden')));

As per path instructions to review HTML templates for accessibility (ARIA attributes, semantic elements).

🤖 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 54 - 59, Add aria-haspopup="true",
aria-expanded="false", and aria-controls="profile-dropdown" to the profile-menu
trigger button. Update toggleProfileDropdown() so the button’s aria-expanded
value mirrors whether profile-dropdown is currently visible after each toggle.

Source: Path instructions

tests/test_api_auth.py (1)

247-274: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

DB mock only supplies 3 statements for what should be 4 reads across two successful logins.

test_login_is_rate_limited_per_ip now needs 2 successful logins (req1, req2) before the 3rd is rate-limited, and each successful login does 2 DB reads (user lookup + avatar lookup per the updated api_login). Only 3 make_stmt(first=row) entries are supplied instead of 4. The test still passes only because MockDB.prepare() silently falls back to a default empty statement once the list is exhausted — def prepare(self, _sql): if self._idx < len(self._stmts): stmt = self._stmts[self._idx] else: stmt = make_stmt() self._idx += 1 return stmt — so this passes "by luck" rather than accurately modeling the new read pattern, unlike the sibling test_login_rate_limit_resets_after_window a few lines below, which was correctly updated to provide 8 stmts for 4 logins.

🧪 Suggested fix
         env.DB = MockDB([
+            make_stmt(first=row),
             make_stmt(first=row),
             make_stmt(first=row),
             make_stmt(first=row),
         ])
🤖 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 `@tests/test_api_auth.py` around lines 247 - 274, Update
test_login_is_rate_limited_per_ip to provide four make_stmt(first=row) entries
in env.DB, covering the user and avatar reads for each of the two successful
login requests; leave the third rate-limited request and its assertions
unchanged.
🤖 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 186-194: Update the authenticated desktop branch alongside
_setNavButtonAvatar to remove the hidden class from cart-nav-link. Also update
refreshCartBadge so its successful cart-context path removes hidden from the
same link, ensuring the cart remains visible after badge refreshes while
preserving hiding behavior when no cart context exists.
- Around line 138-152: Update the avatar handling block to populate
initialsEl.textContent before assigning avatarUrl, and register imgEl.onerror
before setting imgEl.src so immediate load failures use the fallback correctly.
Preserve the existing visibility changes and ensure the handler reveals the
populated initials.
- Around line 102-103: Update the module-scope _navbarPromise and _footerPromise
fetch chains to resolve each response to text immediately and attach rejection
handling so failures cannot become unhandled when inject() is skipped. Adjust
inject() to await the cached text strings directly rather than calling
res.text() on shared Response objects, preserving repeated injection support.

In `@public/profile.html`:
- Around line 172-189: Update the else branch of updatePublicLink to explicitly
reset quick-view-profile when the profile is private: replace its public-profile
href with the appropriate non-public destination or disable it, and change its
label from “View Public Profile” to a label such as “Set up public profile.”
Preserve the existing public-profile href and label behavior in the on branch.
- Line 149: Add an accessible aria-label describing the dismiss action to the
inline close button generated in the toast innerHTML assignment, while
preserving its existing click handler, icon, and styling.
- Around line 65-66: Associate the “Username” label with the username field by
adding a matching for attribute to the label and preserving the existing
username-display input id. Keep the current layout and input behavior unchanged.
- Line 78: Add an explicit maxlength attribute to the how_did_you_hear_about_us
input, matching the established limit used by the other text fields in the form.
Keep its existing id, name, styling, and label unchanged.
- Around line 230-264: Move the image decoding and canvas-processing steps in
handleAvatarFile into the existing try/finally flow so createImageBitmap and
related operations are handled by the same error path as the upload. Ensure
decode or processing failures clear the avatar display, show the existing
user-facing error message, and always reset input.value through finally.
- Line 20: Add role="status" and aria-live="polite" to the dynamically updated
`#flash`, `#profile-status`, and `#delete-account-status` elements so screen readers
announce toast and asynchronous success/error feedback.

In `@public/public-profile.html`:
- Around line 84-87: Update the missing-username early-return path around
showError to hide the state-loading block before displaying the error, matching
the other exit paths. Prefer centralizing hideBlock('state-loading') inside
showError so every error state consistently removes the loading skeleton.

In `@public/users.html`:
- Around line 121-123: Replace the inline onerror fallback in the avatarEl
markup with a safe data attribute on the image, such as the escaped initials
value, and identify it with the avatar-img class. In renderGrid(), immediately
after grid.innerHTML is assigned, attach delegated error listeners to
grid.querySelectorAll('img.avatar-img'); each handler should create a span with
the existing classes, set its text via textContent from the data attribute
(falling back to '?'), and replace the failed image.

In `@src/worker.py`:
- Around line 7138-7141: Update the base64.b64decode call in the image-data
decoding try block to pass validate=True, ensuring malformed or non-alphabet
characters raise an error and follow the existing err("Invalid image data —
expected base64-encoded content") path.
- Around line 7241-7263: Update api_list_users to paginate the public member
directory query: accept validated page/offset and page-size parameters, apply a
bounded maximum page size with SQL LIMIT and offset, and return pagination
metadata alongside the current users list. Preserve the existing public-profile
filtering, ordering, decryption, and response fields while ensuring no request
can retrieve the entire directory.
- Around line 7146-7176: Update the R2 upload call in the avatar upload flow
around r2.put to include httpMetadata with contentType set to the existing
image_type value. Preserve the current key generation, error handling, and
fallback behavior while ensuring stored avatars retain their MIME type for
serve_r2_media().

---

Outside diff comments:
In `@public/js/layout.js`:
- Around line 26-39: Update the catch fallback in getGuestCartToken to generate
the token using window.crypto.getRandomValues with the same cryptographically
secure byte approach as the primary path, rather than Date.now() and
Math.random(). Preserve the existing gct_ prefix and return behavior when
storage is unavailable.

In `@public/partials/navbar.html`:
- Around line 54-59: Add aria-haspopup="true", aria-expanded="false", and
aria-controls="profile-dropdown" to the profile-menu trigger button. Update
toggleProfileDropdown() so the button’s aria-expanded value mirrors whether
profile-dropdown is currently visible after each toggle.

In `@tests/test_api_auth.py`:
- Around line 247-274: Update test_login_is_rate_limited_per_ip to provide four
make_stmt(first=row) entries in env.DB, covering the user and avatar reads for
each of the two successful login requests; leave the third rate-limited request
and its assertions unchanged.
🪄 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 Plus

Run ID: 4e0e07a2-2f92-4634-9889-14f9d7fb2b42

📥 Commits

Reviewing files that changed from the base of the PR and between e53baf8 and 10675db.

📒 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 Outdated
Comment thread public/js/layout.js
Comment thread public/js/layout.js
Comment thread public/profile.html Outdated
Comment thread public/profile.html Outdated
Comment thread public/public-profile.html
Comment thread public/users.html
Comment thread src/worker.py
Comment thread src/worker.py
Comment thread src/worker.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants