| ## π MESSAGE FOR FRONTEND DEVELOPER | |
| ### What changed in the backend: | |
| 1. **User data now has a new field: `profile_photo_url`** β This is the user's Telegram profile photo URL. It can be `null` if the user has no Telegram profile photo. | |
| 2. **New API endpoints available:** | |
| - `GET /api/me/profile` β Returns `{ username, profile_photo_url, student_type, balance, telegram_user_id }` | |
| - `GET /api/user/<username>/profile` β Returns `{ username, profile_photo_url, student_type }` (for other users, used in notifications/sharing) | |
| - `GET /me` (existing, updated) β Now also returns `profile_photo_url` field | |
| 3. **Notifications now include `from_profile_photo_url`** β When a user shares a board, the notification object sent to the recipient now has a `from_profile_photo_url` field (can be `null`). | |
| 4. **Dashboard template now receives `profile_photo_url` variable** β Passed from backend as a template variable. | |
| 5. **After signup β verification β success, user goes directly to dashboard** (no longer redirected to login page). | |
| --- | |
| ### Pages that need frontend updates: | |
| #### 1. `templates/dashboard.html` | |
| - **Display user's profile photo** next to their username/greeting area | |
| - Backend passes `profile_photo_url` (can be `null`) | |
| - **Fallback rule:** If `profile_photo_url` is `null` or empty, show a circle with the **first letter of `username`** (uppercase) as the avatar, with a colored background | |
| - Example: User "ahmed" with no photo β show a circle with letter "A" | |
| - The variable is available as `{{ profile_photo_url }}` in Jinja2 and `{{ username }}` for the fallback letter | |
| #### 2. `templates/chat.html` | |
| - **Display user's profile photo as their chat icon** next to their messages | |
| - Fetch profile from `GET /me` endpoint (already called on page load) β it now returns `profile_photo_url` | |
| - **Fallback rule:** Same as dashboard β if `null`, use first letter of username in a colored circle | |
| - The AI/teacher messages keep their existing icon (no change) | |
| #### 3. **Notifications display** (wherever notifications are rendered β likely in `dashboard.html` or a notifications panel) | |
| - Each notification of type `board_share` now has `from_profile_photo_url` field | |
| - Display the **sender's profile photo** next to the notification text | |
| - **Fallback rule:** If `from_profile_photo_url` is `null`, use first letter of `from_username` in a colored circle | |
| - The `from_username` field is already present in notifications | |
| #### 4. **Board share UI** (in `templates/board.html` if there's a share dialog) | |
| - When searching users via `GET /api/users/search`, the results include `username` and `student_type` | |
| - To show profile photos in search results, make an additional call to `GET /api/user/<username>/profile` for each result, OR just use the first-letter fallback (simpler) | |
| --- | |
| ### Avatar fallback logic (use everywhere): | |
| ```javascript | |
| // Helper function for all pages | |
| function getAvatarHTML(profilePhotoUrl, username, size = 40) { | |
| if (profilePhotoUrl) { | |
| return `<img src="${profilePhotoUrl}" alt="${username}" | |
| style="width:${size}px; height:${size}px; border-radius:50%; object-fit:cover;" | |
| onerror="this.outerHTML=getLetterAvatarHTML('${username}', ${size})">`; | |
| } | |
| return getLetterAvatarHTML(username, size); | |
| } | |
| function getLetterAvatarHTML(username, size = 40) { | |
| const letter = (username && username.length > 0) ? username.charAt(0).toUpperCase() : '?'; | |
| // Generate consistent color from username | |
| const colors = ['#7C3AED', '#0EA5E9', '#10B981', '#F59E0B', '#EF4444', '#EC4899', '#8B5CF6', '#06B6D4']; | |
| let hash = 0; | |
| for (let i = 0; i < username.length; i++) { | |
| hash = username.charCodeAt(i) + ((hash << 5) - hash); | |
| } | |
| const color = colors[Math.abs(hash) % colors.length]; | |
| const fontSize = Math.round(size * 0.45); | |
| return `<div style="width:${size}px; height:${size}px; border-radius:50%; | |
| background:${color}; display:flex; align-items:center; justify-content:center; | |
| color:white; font-weight:700; font-size:${fontSize}px; font-family:'Cairo',sans-serif; | |
| flex-shrink:0;">${letter}</div>`; | |
| } | |
| ``` | |
| ### Jinja2 macro for templates (use in dashboard.html and anywhere server-rendered): | |
| ```html | |
| {# Place this at the top of any template that needs avatars #} | |
| {% macro avatar(photo_url, username, size=40) %} | |
| {% if photo_url %} | |
| <img src="{{ photo_url }}" alt="{{ username }}" | |
| style="width:{{ size }}px; height:{{ size }}px; border-radius:50%; object-fit:cover;" | |
| onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';"> | |
| <div style="width:{{ size }}px; height:{{ size }}px; border-radius:50%; | |
| background:#7C3AED; display:none; align-items:center; justify-content:center; | |
| color:white; font-weight:700; font-size:{{ (size * 0.45)|int }}px; font-family:'Cairo',sans-serif;"> | |
| {{ username[0]|upper if username else '?' }} | |
| </div> | |
| {% else %} | |
| <div style="width:{{ size }}px; height:{{ size }}px; border-radius:50%; | |
| background:#7C3AED; display:flex; align-items:center; justify-content:center; | |
| color:white; font-weight:700; font-size:{{ (size * 0.45)|int }}px; font-family:'Cairo',sans-serif;"> | |
| {{ username[0]|upper if username else '?' }} | |
| </div> | |
| {% endif %} | |
| {% endmacro %} | |
| ``` | |
| ### Summary of where to use avatars: | |
| | Page | Where | Data source | Photo field | Username field | | |
| |------|-------|-------------|-------------|----------------| | |
| | `dashboard.html` | User greeting area (top) | Template variable | `{{ profile_photo_url }}` | `{{ username }}` | | |
| | `chat.html` | Next to user's own messages | JS from `GET /me` response | `response.profile_photo_url` | `response.username` | | |
| | `dashboard.html` or notification panel | Each notification item | Notification object | `notif.from_profile_photo_url` | `notif.from_username` | | |
| | `board.html` | Share dialog user search results | Use first-letter fallback | N/A (not fetched) | `result.username` | | |
| ### Important notes: | |
| - `profile_photo_url` CAN be `null` β always handle the fallback | |
| - The `onerror` handler on `<img>` is important because even if a URL exists, it might be expired or broken (Telegram URLs can expire) | |
| - The `GET /api/user/<username>/profile` endpoint exists if you ever need to fetch another user's photo on demand, but for notifications the `from_profile_photo_url` is already included in the notification data | |
| - No changes needed to `verification.html` β it works correctly now | |
| - No changes needed to `signup.html` or `login.html` |