// DEPLOY_CONFIG: {"triggers": [{"name": "meeting_time_confirmed_email_participants", "on": "collection.add", "collection": "meeting_times", "actions": [{"type": "email", "to": "{{participants_emails}}", "subject": "Meeting time confirmed", "body": "The meeting time has been confirmed for {{confirmed_time_localized}}. Each recipient should see this time in their own local time zone."}]}]} import { useEffect, useMemo, useState } from 'react'; import { Timeline } from './components/Timeline.jsx'; import { MemberManager } from './components/MemberManager.jsx'; import { OverlapFinder } from './components/OverlapFinder.jsx'; import { useAI, useAuth, useCollection, usePresence } from '@deplixo/sdk'; function App() { const [activeTab, setActiveTab] = useState('timeline'); const [selectedGroupId, setSelectedGroupId] = useState(''); const [shareFeedback, setShareFeedback] = useState(''); const { items: members, loading: membersLoading, add, update, remove } = useCollection('members', { personal: true }); const { items: meetingGroups, loading: groupsLoading, add: addGroup, update: updateGroup, remove: removeGroup } = useCollection('meeting_groups', { personal: true }); const { items: meetings, loading: meetingsLoading, add: addMeeting, update: updateMeeting, remove: removeMeeting } = useCollection('meetings', { personal: true }); const { user, loading: authLoading, login, logout } = useAuth(); const { users: onlineUsers, update: updatePresence } = usePresence({ status: 'online' }); const { generate, loading: aiLoading, error: aiError } = useAI(); const [meetingSuggestion, setMeetingSuggestion] = useState(null); const [suggestionFeedback, setSuggestionFeedback] = useState(''); const [suggestionLoading, setSuggestionLoading] = useState(false); const tabs = [ { id: 'timeline', label: '⏱ Timeline', icon: '⏱' }, { id: 'members', label: '👥 Members', icon: '👥' }, { id: 'overlap', label: '✦ Find Time', icon: '✦' }, { id: 'calendar', label: '🗓 Calendar', icon: '🗓' }, ]; const getSchedulerShareLink = (groupId = '') => { const url = new URL(window.location.href); url.searchParams.set('view', 'overlap'); if (groupId) { url.searchParams.set('group', groupId); } else { url.searchParams.delete('group'); } return url.toString(); }; const syncStateFromUrl = () => { if (typeof window === 'undefined') return; const params = new URLSearchParams(window.location.search); const view = params.get('view'); const group = params.get('group') || ''; if (view && ['timeline', 'members', 'overlap', 'calendar'].includes(view)) { setActiveTab(view); } setSelectedGroupId(group); }; const handleAuthAction = async () => { if (user) { await logout(); return; } await login('google'); }; const isAuthenticated = !!user; const isLoading = authLoading || membersLoading || groupsLoading || meetingsLoading; const selectedGroup = useMemo(() => { return meetingGroups?.find(group => group.id === selectedGroupId) || null; }, [meetingGroups, selectedGroupId]); const selectedGroupShareLink = useMemo(() => getSchedulerShareLink(selectedGroupId), [selectedGroupId]); const onlineMemberIds = useMemo(() => { return new Set((onlineUsers || []).map(u => u.id)); }, [onlineUsers]); const onlineMembers = useMemo(() => { return (members || []).filter(member => { if (member?.id && onlineMemberIds.has(member.id)) return true; if (member?.userId && onlineMemberIds.has(member.userId)) return true; return false; }); }, [members, onlineMemberIds]); useEffect(() => { if (!isAuthenticated || !user) return; updatePresence({ id: user.id, name: user.name || 'Signed in user', avatar: user.avatar || '', status: 'online', activeTab, groupId: selectedGroupId, updatedAt: new Date().toISOString(), }); }, [isAuthenticated, user, activeTab, selectedGroupId, updatePresence]); useEffect(() => { if (typeof window === 'undefined') return undefined; const markActive = () => { if (!user) return; updatePresence({ id: user.id, name: user.name || 'Signed in user', avatar: user.avatar || '', status: 'online', activeTab, groupId: selectedGroupId, updatedAt: new Date().toISOString(), }); }; const events = ['focus', 'visibilitychange', 'mousemove', 'keydown', 'click', 'touchstart']; events.forEach(eventName => window.addEventListener(eventName, markActive, { passive: true })); const intervalId = window.setInterval(markActive, 30000); return () => { events.forEach(eventName => window.removeEventListener(eventName, markActive)); window.clearInterval(intervalId); }; }, [user, activeTab, selectedGroupId, updatePresence]); const handleSelectGroup = (groupId) => { setSelectedGroupId(groupId); const url = new URL(window.location.href); url.searchParams.set('view', 'overlap'); if (groupId) { url.searchParams.set('group', groupId); } else { url.searchParams.delete('group'); } window.history.replaceState({}, '', url.toString()); }; const handleCopyShareLink = async () => { const shareLink = selectedGroupId ? selectedGroupShareLink : getSchedulerShareLink(''); try { await navigator.clipboard.writeText(shareLink); setShareFeedback('Scheduler link copied to clipboard'); } catch { setShareFeedback('Copy failed — share this link manually'); } window.clearTimeout(handleCopyShareLink._timer); handleCopyShareLink._timer = window.setTimeout(() => setShareFeedback(''), 2500); }; const handleSaveGroup = async (groupData) => { const payload = { ...groupData, memberIds: Array.isArray(groupData.memberIds) ? groupData.memberIds : [], minDuration: Number(groupData.minDuration) || 1, displayTimezone: groupData.displayTimezone || 'UTC', updatedAt: new Date().toISOString(), }; if (groupData.id) { await updateGroup(groupData.id, payload); return groupData.id; } const created = await addGroup({ ...payload, createdAt: new Date().toISOString(), }); return created?.id || ''; }; const handleDeleteGroup = async (groupId) => { await removeGroup(groupId); if (selectedGroupId === groupId) { setSelectedGroupId(''); const url = new URL(window.location.href); url.searchParams.delete('group'); window.history.replaceState({}, '', url.toString()); } }; const handleSaveMeeting = async (meetingData) => { const payload = { ...meetingData, memberIds: Array.isArray(meetingData.memberIds) ? meetingData.memberIds : [], timezone: meetingData.timezone || 'UTC', startAt: meetingData.startAt || new Date().toISOString(), endAt: meetingData.endAt || new Date(Date.now() + 60 * 60 * 1000).toISOString(), updatedAt: new Date().toISOString(), }; if (meetingData.id) { await updateMeeting(meetingData.id, payload); return meetingData.id; } const created = await addMeeting({ ...payload, createdAt: new Date().toISOString(), }); return created?.id || ''; }; const handleDeleteMeeting = async (meetingId) => { await removeMeeting(meetingId); }; const buildSuggestedMeetingPrompt = (group, groupMembers, existingMeetings) => { const memberLines = (groupMembers || []).map(member => { const hours = `${member?.workingHoursStart ?? 9}:00-${member?.workingHoursEnd ?? 17}:00`; return `- ${member?.name || 'Unknown'} | timezone: ${member?.timezone || 'UTC'} | working hours: ${hours}`; }).join('\n'); const meetingLines = (existingMeetings || []).slice(0, 40).map(meeting => { return `- ${meeting?.title || 'Untitled'} | start: ${meeting?.startAt || ''} | end: ${meeting?.endAt || ''} | timezone: ${meeting?.timezone || 'UTC'}`; }).join('\n'); return { system: 'You are a scheduling assistant. Propose the best meeting time based on member working hours, existing meetings, and the requested duration. Return JSON only.', user: `Meeting group: ${group?.name || 'Selected group'}\nMinimum duration (minutes): ${Number(group?.minDuration) || 60}\nDisplay timezone: ${group?.displayTimezone || 'UTC'}\n\nMembers:\n${memberLines || '- No members selected'}\n\nExisting meetings:\n${meetingLines || '- None'}\n\nReturn JSON with this exact shape: {"startAt":"ISO string","endAt":"ISO string","timezone":"IANA/UTC timezone string","confidence":"high|medium|low","reason":"short explanation","memberAvailability":"short summary"}. Choose the earliest high-quality slot within the next 14 days if possible. Prefer times that maximize overlap and avoid conflicts.`, }; }; const handleGenerateMeetingSuggestion = async () => { const groupMembers = selectedGroup ? (members || []).filter(member => (selectedGroup.memberIds || []).includes(member.id) || (selectedGroup.memberIds || []).includes(member.userId)) : (members || []); const durationMinutes = Number(selectedGroup?.minDuration) || 60; if (!groupMembers.length) { setMeetingSuggestion(null); setSuggestionFeedback('Select a meeting group with members before asking for an AI suggestion.'); return; } setSuggestionLoading(true); setSuggestionFeedback(''); try { const response = await generate(buildSuggestedMeetingPrompt(selectedGroup, groupMembers, meetings || [])); const parsed = typeof response === 'string' ? JSON.parse(response) : response; if (!parsed?.startAt || !parsed?.endAt) { setMeetingSuggestion(null); setSuggestionFeedback('AI suggestion was incomplete. Try again.'); } else { setMeetingSuggestion({ ...parsed, durationMinutes, groupId: selectedGroupId, }); } } catch { setMeetingSuggestion(null); setSuggestionFeedback('AI suggestion failed. Please try again.'); } finally { setSuggestionLoading(false); } }; const handleApplySuggestion = () => { if (!meetingSuggestion) return; window.dispatchEvent(new CustomEvent('synczone-open-meeting-draft', { detail: meetingSuggestion })); setActiveTab('calendar'); }; if (typeof window !== 'undefined') { const expectedView = new URLSearchParams(window.location.search).get('view'); const expectedGroup = new URLSearchParams(window.location.search).get('group') || ''; if (expectedView || expectedGroup) { queueMicrotask(syncStateFromUrl); } } return (

SyncZone

Distributed Team Scheduler

Online now
{onlineMembers.length ? ( onlineMembers.map(member => ( {member.name} )) ) : ( No teammates online )}
{authLoading ? (
Checking sign-in…
) : isAuthenticated ? (
{user?.avatar ? ( {user?.name ) : (
{(user?.name || 'U').slice(0, 1).toUpperCase()}
)}
{user?.name || 'Signed in'} Authenticated
) : ( )}
{isLoading ? (
) : ( <> {activeTab === 'timeline' && } {activeTab === 'members' && ( isAuthenticated ? ( ) : (

Sign in required

Use Google sign-in to manage your team members and access the authenticated workspace.

) )} {activeTab === 'overlap' && (

Find Time

Share this scheduler link with your team so everyone can open the same meeting group and planning view.

{shareFeedback ?
{shareFeedback}
: null}
)} {activeTab === 'calendar' && ( )} )}
); } function GoogleAuthGate({ user, loading, onSignIn, onSignOut, children }) { if (loading) { return (
Checking sign-in…
); } if (!user) { return (

Sign in required

Use Google sign-in to access team management and the authenticated workspace.

); } return ( <> {children} ); } function CalendarView({ meetings, members, meetingGroups, currentUser, selectedGroupId, onSelectGroup, onSaveMeeting, onDeleteMeeting, meetingSuggestion, onGenerateSuggestion, onApplySuggestion, aiLoading, aiError, suggestionFeedback }) { const [calendarMonth, setCalendarMonth] = useState(() => new Date()); const [draftMeeting, setDraftMeeting] = useState(null); const [expandedMeetingId, setExpandedMeetingId] = useState(''); useEffect(() => { const openDraft = (event) => { const detail = event?.detail; if (!detail) return; setDraftMeeting({ id: '', title: detail.title || 'AI-suggested meeting', notes: detail.reason || '', memberIds: Array.isArray(detail.memberIds) ? detail.memberIds : [], timezone: detail.timezone || 'UTC', startAt: detail.startAt || new Date().toISOString(), endAt: detail.endAt || new Date(Date.now() + 60 * 60 * 1000).toISOString(), groupId: detail.groupId || selectedGroupId || '', }); }; window.addEventListener('synczone-open-meeting-draft', openDraft); return () => window.removeEventListener('synczone-open-meeting-draft', openDraft); }, [selectedGroupId]); const monthStart = useMemo(() => { return new Date(calendarMonth.getFullYear(), calendarMonth.getMonth(), 1); }, [calendarMonth]); const monthEnd = useMemo(() => { return new Date(calendarMonth.getFullYear(), calendarMonth.getMonth() + 1, 0); }, [calendarMonth]); const calendarDays = useMemo(() => { const start = new Date(monthStart); start.setDate(start.getDate() - start.getDay()); const days = []; for (let i = 0; i < 42; i += 1) { const day = new Date(start); day.setDate(start.getDate() + i); days.push(day); } return days; }, [monthStart]); const visibleMeetings = useMemo(() => { return (meetings || []) .filter(meeting => { if (!meeting?.startAt) return false; const start = new Date(meeting.startAt); return start >= monthStart && start <= new Date(monthEnd.getFullYear(), monthEnd.getMonth(), monthEnd.getDate(), 23, 59, 59, 999); }) .sort((a, b) => new Date(a.startAt).getTime() - new Date(b.startAt).getTime()); }, [meetings, monthStart, monthEnd]); const selectedGroup = useMemo(() => { return meetingGroups?.find(group => group.id === selectedGroupId) || null; }, [meetingGroups, selectedGroupId]); const selectedGroupMembers = useMemo(() => { const selectedIds = new Set((selectedGroup?.memberIds || []).filter(Boolean)); if (!selectedIds.size) return members || []; return (members || []).filter(member => selectedIds.has(member.id) || selectedIds.has(member.userId)); }, [members, selectedGroup]); const goMonth = (delta) => { setCalendarMonth(prev => new Date(prev.getFullYear(), prev.getMonth() + delta, 1)); }; const handleSave = async (meetingData) => { if (!onSaveMeeting) return; const savedId = await onSaveMeeting(meetingData); if (savedId) { setDraftMeeting(null); setExpandedMeetingId(savedId); } }; const eventsByDate = useMemo(() => { const map = new Map(); (visibleMeetings || []).forEach(meeting => { const key = formatDateKey(meeting.startAt); if (!map.has(key)) map.set(key, []); map.get(key).push(meeting); }); return map; }, [visibleMeetings]); const suggestionDuration = meetingSuggestion?.startAt && meetingSuggestion?.endAt ? Math.max(15, Math.round((new Date(meetingSuggestion.endAt).getTime() - new Date(meetingSuggestion.startAt).getTime()) / 60000)) : Number(selectedGroup?.minDuration) || 60; const suggestionStartLabel = meetingSuggestion?.startAt ? formatMeetingDateTime(meetingSuggestion.startAt, meetingSuggestion?.timezone || selectedGroup?.displayTimezone || 'UTC') : ''; const suggestionEndLabel = meetingSuggestion?.endAt ? formatMeetingDateTime(meetingSuggestion.endAt, meetingSuggestion?.timezone || selectedGroup?.displayTimezone || 'UTC') : ''; return (

Calendar

Time zone-aware meeting planning with local rendering for each member.

{formatMonthLabel(calendarMonth)}

AI meeting suggestion

Generate an optimal time from existing members, selected group settings, and current calendar conflicts.

{aiError ?
{aiError}
: null} {suggestionFeedback ?
{suggestionFeedback}
: null} {meetingSuggestion ? (
Suggested slot
{suggestionStartLabel} → {suggestionEndLabel}
{meetingSuggestion.timezone || selectedGroup?.displayTimezone || 'UTC'} · {suggestionDuration} min · confidence {meetingSuggestion.confidence || 'medium'}
{meetingSuggestion.reason || 'Best available time based on current constraints.'}
{meetingSuggestion.memberAvailability || ''}
) : null}
{['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
{day}
))}
{calendarDays.map(day => { const key = formatDateKey(day); const dayMeetings = eventsByDate.get(key) || []; const isCurrentMonth = day.getMonth() === calendarMonth.getMonth(); const isToday = formatDateKey(day) === formatDateKey(new Date()); return (
{day.getDate()} {dayMeetings.length ? {dayMeetings.length} : null}
{dayMeetings.slice(0, 3).map(meeting => ( ))} {dayMeetings.length > 3 ?
+{dayMeetings.length - 3} more
: null}
); })}

Upcoming meetings

{visibleMeetings.length} total
{visibleMeetings.length ? visibleMeetings.map(meeting => (
{expandedMeetingId === meeting.id ? (
Start {formatMeetingDateTime(meeting.startAt, selectedGroup?.displayTimezone || meeting.timezone || 'UTC')}
End {formatMeetingDateTime(meeting.endAt, selectedGroup?.displayTimezone || meeting.timezone || 'UTC')}
Members {(meeting.memberIds || []).length || 'All available'}
{(meeting.memberIds || []).map(memberId => { const member = (members || []).find(item => item.id === memberId || item.userId === memberId); if (!member) return null; return ( {member.name} ); })}
) : null}
)) : (
🗓

No meetings yet

Create a meeting to see it appear in this calendar grid and list with time zone-aware rendering.

)}
{draftMeeting ? (
setDraftMeeting(null)} currentUser={currentUser} />
) : null}
); } function MeetingForm({ meeting, members, onSave, onCancel }) { const [formState, setFormState] = useState(() => ({ id: meeting?.id || '', title: meeting?.title || '', notes: meeting?.notes || '', memberIds: Array.isArray(meeting?.memberIds) ? meeting.memberIds : [], timezone: meeting?.timezone || 'UTC', startAt: meeting?.startAt || new Date().toISOString(), endAt: meeting?.endAt || new Date(Date.now() + 60 * 60 * 1000).toISOString(), groupId: meeting?.groupId || '', })); useEffect(() => { setFormState({ id: meeting?.id || '', title: meeting?.title || '', notes: meeting?.notes || '', memberIds: Array.isArray(meeting?.memberIds) ? meeting.memberIds : [], timezone: meeting?.timezone || 'UTC', startAt: meeting?.startAt || new Date().toISOString(), endAt: meeting?.endAt || new Date(Date.now() + 60 * 60 * 1000).toISOString(), groupId: meeting?.groupId || '', }); }, [meeting]); const toggleMember = (memberId) => { setFormState(prev => { const exists = prev.memberIds.includes(memberId); return { ...prev, memberIds: exists ? prev.memberIds.filter(id => id !== memberId) : [...prev.memberIds, memberId], }; }); }; const submit = async (event) => { event.preventDefault(); await onSave?.(formState); }; return (

{formState.id ? 'Edit meeting' : 'New meeting'}

setFormState(prev => ({ ...prev, title: event.target.value }))} placeholder="Team sync" />
setFormState(prev => ({ ...prev, timezone: event.target.value }))} placeholder="UTC" />
setFormState(prev => ({ ...prev, startAt: toIsoFromLocalInput(event.target.value, prev.timezone) }))} />
setFormState(prev => ({ ...prev, endAt: toIsoFromLocalInput(event.target.value, prev.timezone) }))} />
{(members || []).map(member => ( ))}