// DEPLOY_CONFIG: {"cron": [{"name": "action-item-due-reminder", "schedule": "0 9 * * 1-5", "action": "event", "config": {"event_type": "action_items_due_reminder"}}], "triggers": []} import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useAuth, useBroadcast, useCollection, usePresence } from '@deplixo/sdk'; import { MeetingList } from './components/MeetingList.jsx'; import { MeetingDetail } from './components/MeetingDetail.jsx'; import { ActionItemsDashboard } from './components/ActionItemsDashboard.jsx'; function App() { const { user, loading, logout } = useAuth(); const { items: userProfileItems, loading: profileLoading } = useCollection('userProfiles', { personal: true }); const { items: noteStates, add: addNoteState, update: updateNoteState } = useCollection('meetingNoteStates', { personal: true }); const [activeTab, setActiveTab] = useState('meetings'); const [selectedMeetingId, setSelectedMeetingId] = useState(null); const [meetingSyncVersion, setMeetingSyncVersion] = useState({}); const signedInUser = user || null; const userProfile = userProfileItems?.find((profile) => profile?.email === signedInUser?.email || profile?.userId === signedInUser?.id) || null; const appUser = signedInUser ? { id: signedInUser.id, name: signedInUser.name, email: signedInUser.email, avatar: signedInUser.avatar, profile: userProfile, } : null; const userId = appUser?.id || appUser?.email || 'anonymous'; const selectedMeetingState = useMemo(() => { if (!selectedMeetingId || !noteStates?.length) return null; return noteStates.find((state) => state?.meetingId === selectedMeetingId) || null; }, [noteStates, selectedMeetingId]); const meetingChannel = useMemo(() => selectedMeetingId ? `meeting-notes:${selectedMeetingId}` : null, [selectedMeetingId]); const presenceChannel = useMemo(() => selectedMeetingId ? `meeting-presence:${selectedMeetingId}` : null, [selectedMeetingId]); const broadcastHandler = useCallback((data) => { if (!data || !data.meetingId || data.meetingId !== selectedMeetingId) return; if (data.type === 'state-update' && data.state) { const incomingVersion = Number(data.state.version || 0); setMeetingSyncVersion((prev) => { const currentVersion = Number(prev[selectedMeetingId] || 0); if (incomingVersion > currentVersion) { return { ...prev, [selectedMeetingId]: incomingVersion }; } return prev; }); } }, [selectedMeetingId]); const { send: sendMeetingBroadcast } = useBroadcast(meetingChannel || 'meeting-notes:idle', broadcastHandler); const lastProcessedBroadcastRef = useRef(null); useEffect(() => { if (!selectedMeetingState?.meetingId) return; const version = Number(selectedMeetingState.version || 0); setMeetingSyncVersion((prev) => { if (prev[selectedMeetingState.meetingId] === version) return prev; return { ...prev, [selectedMeetingState.meetingId]: version }; }); }, [selectedMeetingState]); const persistMeetingState = useCallback((meetingId, patch) => { if (!meetingId) return; const existing = noteStates?.find((state) => state?.meetingId === meetingId) || null; const baseVersion = Number(existing?.version || meetingSyncVersion[meetingId] || 0); const nextVersion = baseVersion + 1; const payload = { meetingId, ...existing, ...patch, version: nextVersion, updatedAt: new Date().toISOString(), updatedBy: userId, }; if (existing?.id) { updateNoteState(existing.id, payload); } else { addNoteState(payload); } setMeetingSyncVersion((prev) => ({ ...prev, [meetingId]: nextVersion })); sendMeetingBroadcast({ type: 'state-update', meetingId, state: payload, authorId: userId, }); }, [addNoteState, meetingSyncVersion, noteStates, sendMeetingBroadcast, updateNoteState, userId]); const handleMeetingStateChange = useCallback((meetingId, patch) => { persistMeetingState(meetingId, patch); }, [persistMeetingState]); const handleSelectMeeting = (id) => { setSelectedMeetingId(id); setActiveTab('detail'); }; const handleBack = () => { setSelectedMeetingId(null); setActiveTab('meetings'); }; // Presence: track who is currently viewing the selected meeting const currentMeetingPresence = usePresence( selectedMeetingId ? { userId: appUser?.id || userId, name: appUser?.name || 'Anonymous', email: appUser?.email, avatar: appUser?.avatar, meetingId: selectedMeetingId, status: 'in-meeting', } : null ); const presenceUsers = currentMeetingPresence?.users || []; const presenceCount = presenceUsers.length; const presenceLabel = presenceCount === 0 ? 'No one else is currently in this meeting' : presenceCount === 1 ? '1 person is currently in this meeting' : `${presenceCount} people are currently in this meeting`; // PROGRESS:sc_001:complete:Setting up meeting management // PROGRESS:sc_002:complete:Building rich text notes editor // PROGRESS:sc_003:complete:Creating action items tracker if (loading || profileLoading) { return
Signing in...
; } if (!user) { return (

📋 Meeting Notes

🔐

Team access required

Sign in with Google to join your team workspace and attribute meetings, notes, and action items to your account.

Continue with Google
); } return (

📋 Meeting Notes

{appUser?.name}
{activeTab === 'detail' && ( )}
{activeTab !== 'detail' && ( )}
{activeTab === 'meetings' && ( )} {activeTab === 'detail' && selectedMeetingId && (
Meeting presence
{presenceLabel}
{presenceUsers.length > 0 && (
{presenceUsers.map((presenceUser, index) => { const key = presenceUser?.id || presenceUser?.userId || presenceUser?.email || `${presenceUser?.name || 'user'}-${index}`; const displayName = presenceUser?.name || presenceUser?.email || 'Anonymous'; const avatarText = (displayName || 'A').slice(0, 1).toUpperCase(); return (
{presenceUser?.avatar ? ( {displayName} ) : ( {avatarText} )} {displayName}
); })}
)}
)} {activeTab === 'actions' && ( )}
); } ReactDOM.createRoot(document.getElementById("root")).render();