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
+17
View File
@@ -0,0 +1,17 @@
import { useEffect, useRef } from 'react';
import { supabase } from '../lib/supabase';
export function useRealtimeSubscription(tables, onchange) {
const onchangeRef = useRef(onchange);
useEffect(() => { onchangeRef.current = onchange; });
useEffect(() => {
const channelName = `rt-${tables.join('-')}-${Date.now()}`;
let channel = supabase.channel(channelName);
for (const table of tables) {
channel = channel.on('postgres_changes', { event: '*', schema: 'public', table }, () => onchangeRef.current());
}
channel.subscribe();
return () => { supabase.removeChannel(channel); };
}, []); // eslint-disable-line react-hooks/exhaustive-deps
}
+12
View File
@@ -0,0 +1,12 @@
import { useEffect, useRef } from 'react';
export function useRefetchOnFocus(refetch) {
const refetchRef = useRef(refetch);
useEffect(() => { refetchRef.current = refetch; });
useEffect(() => {
const onFocus = () => refetchRef.current();
window.addEventListener('focus', onFocus);
return () => window.removeEventListener('focus', onFocus);
}, []);
}