feat: project detail overhaul, realtime updates, avatar RLS fix

- ProjectDetail: full redesign — icon meta row, completion progress bar, main contact card, activity feed, subcontractor management modal with multi-select
- ProjectDetail: task table matches Tasks.jsx (R#, assigned avatar, priority, type, deadline, status) with status tabs + counts
- Realtime: useRefetchOnFocus + useRealtimeSubscription hooks wired to Tasks, TaskDetail, ProjectDetail, TeamDashboard
- AuthContext: live profile updates via Supabase realtime channel
- Tasks/ProjectDetail: assignee avatar join added for all roles (client, external, team)
- RLS: allow all authenticated users to read profiles (fixes avatar display across roles)
- RequestForm: lockedFields prop for pre-filled read-only company/project fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-05-31 16:55:58 -04:00
parent 35d92d2176
commit 70ad8d0cef
8 changed files with 546 additions and 203 deletions
+8 -2
View File
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef, useCallback } from 'react';
import { useParams, Link } from 'react-router-dom';
import Layout from '../components/Layout';
import PageLoader from '../components/PageLoader';
@@ -11,6 +11,8 @@ import JSZip from 'jszip';
import { popupOverlayStyle, popupSurfaceStyle } from '../lib/popupStyles';
import { sendEmail } from '../lib/email';
import { uploadFilesToRequestInfo, safeName } from '../lib/filebrowserFolders';
import { useRefetchOnFocus } from '../hooks/useRefetchOnFocus';
import { useRealtimeSubscription } from '../hooks/useRealtimeSubscription';
const ACTION_LABEL = {
task_created: 'created this task', task_started: 'started this task', task_on_hold: 'placed task on hold',
@@ -201,6 +203,10 @@ function SubmissionFiles({ files, downloading, onDownloadAll }) {
export default function TaskDetail() {
const { id } = useParams();
const { currentUser } = useAuth();
const [refreshKey, setRefreshKey] = useState(0);
const refresh = useCallback(() => setRefreshKey(k => k + 1), []);
useRefetchOnFocus(refresh);
useRealtimeSubscription(['tasks', 'submissions', 'activity_log', 'task_comments'], refresh);
const [task, setTask] = useState(null);
const [submissions, setSubmissions] = useState([]);
const [activityLog, setActivityLog] = useState([]);
@@ -255,7 +261,7 @@ export default function TaskDetail() {
setComments(commentData || []);
setLoading(false);
});
}, [id]);
}, [id, refreshKey]);
if (loading) return <Layout><PageLoader /></Layout>;
if (!task) return <Layout><p style={{ padding: 24 }}>Task not found.</p></Layout>;