Session 2026-05-28: profile page overhaul, nav fixes, dashboard activity links

- Fix nav links not working from profile page (useEffect infinite re-render via unstable profile object ref)
- Fix nav hover/active: gold icon highlight, no background change; active links non-clickable
- Fix hover layout shift: add border: 1px solid transparent to all interactive elements
- Header icon buttons (search, theme toggle) now highlight gold on hover
- Profile page: replace calendar with activity feed (60/40 grid), add stat cards (tasks completed, active projects, revision requests, submissions)
- Profile card: title field, icon rows for location/email/linkedin, member since + role bottom-right, edit button top-right
- Profile portrait: remove wrapper column, fix left-gap alignment
- Add profiles.title migration
- Dashboard recent activity: name → /profile/{id}, task → /requests/{id} (clickable links)
- Icon-only sidebar with gold active/hover state, pointer-events: none on active links
- layout.md updated with profile page geometry rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-05-28 15:32:46 -04:00
parent 565d2ed4bc
commit 283511bf3a
48 changed files with 4151 additions and 1889 deletions
+14
View File
@@ -0,0 +1,14 @@
import { supabase } from './supabase';
export async function logActivity({ actorId, actorName, action, taskId, taskTitle, projectId, projectName }) {
const { error } = await supabase.from('activity_log').insert({
actor_id: actorId || null,
actor_name: actorName || null,
action,
task_id: taskId || null,
task_title: taskTitle || null,
project_id: projectId || null,
project_name: projectName || null,
});
if (error) console.error('logActivity failed:', action, error);
}
+29
View File
@@ -0,0 +1,29 @@
import { useState, useEffect } from 'react';
export function useLiveClock() {
const [now, setNow] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(id);
}, []);
return now;
}
export function DashboardBanner() {
const now = useLiveClock();
const day = now.toLocaleDateString('en-US', { weekday: 'long', timeZone: 'America/New_York' });
const date = now.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric', timeZone: 'America/New_York' });
const time = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', timeZone: 'America/New_York', timeZoneName: 'short' });
return (
<div className="dashboard-banner">
Fourge Branding &bull; {day}, {date} &bull; {time}
</div>
);
}
export function getGreeting() {
const h = new Date().getHours();
if (h < 12) return 'Good morning';
if (h < 17) return 'Good afternoon';
return 'Good evening';
}
+10
View File
@@ -47,3 +47,13 @@ export function formatDateOnly(value, fallback = '—') {
year: 'numeric',
});
}
export function fmtShortDate(value, fallback = '—') {
if (!value) return fallback;
const date = parseDateOnly(value) || new Date(value);
if (isNaN(date)) return fallback;
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
const y = String(date.getFullYear()).slice(2);
return `${m}/${d}/${y}`;
}
+1 -1
View File
@@ -1,4 +1,4 @@
export async function withTimeout(promise, ms = 12000, label = 'Request') {
export async function withTimeout(promise, ms = 25000, label = 'Request') {
let timerId;
try {
return await Promise.race([