T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/private_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/components/CollaborationHub.tsx
import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';

interface TeamMember {
  id: string;
  name: string;
  role: string;
  avatar?: string;
  isOnline: boolean;
  lastSeen: Date;
  currentTask?: string;
}

interface CaseActivity {
  id: string;
  type: 'document_upload' | 'status_change' | 'comment' | 'assignment' | 'deadline';
  description: string;
  user: TeamMember;
  timestamp: Date;
  caseId: string;
}

interface CollaborationHubProps {
  caseId?: string;
}

const CollaborationHub: React.FC<CollaborationHubProps> = ({ caseId }) => {
  const { data: session } = useSession();
  const [teamMembers, setTeamMembers] = useState<TeamMember[]>([]);
  const [activities, setActivities] = useState<CaseActivity[]>([]);
  const [activeUsers, setActiveUsers] = useState<string[]>([]);
  const [selectedView, setSelectedView] = useState<'team' | 'activity' | 'tasks'>('team');

  useEffect(() => {
    if (caseId) {
      fetchTeamMembers();
      fetchActivities();
      setupRealTimeUpdates();
    }
  }, [caseId]);

  const fetchTeamMembers = async () => {
    try {
      const response = await fetch(`/api/cases/${caseId}/team`);
      if (response.ok) {
        const data = await response.json();
        setTeamMembers(data);
      }
    } catch (error) {
      console.error('Error fetching team members:', error);
    }
  };

  const fetchActivities = async () => {
    try {
      const response = await fetch(`/api/cases/${caseId}/activities`);
      if (response.ok) {
        const data = await response.json();
        setActivities(data);
      }
    } catch (error) {
      console.error('Error fetching activities:', error);
    }
  };

  const setupRealTimeUpdates = () => {
    // WebSocket connection for real-time updates
    const ws = new WebSocket(`${process.env.NEXT_PUBLIC_WS_URL}/case/${caseId}`);
    
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      
      switch (data.type) {
        case 'user_online':
          setActiveUsers(prev => [...prev, data.userId]);
          break;
        case 'user_offline':
          setActiveUsers(prev => prev.filter(id => id !== data.userId));
          break;
        case 'activity_update':
          setActivities(prev => [data.activity, ...prev]);
          break;
        case 'team_update':
          fetchTeamMembers();
          break;
      }
    };

    return () => ws.close();
  };

  const getActivityIcon = (type: string) => {
    switch (type) {
      case 'document_upload': return '📄';
      case 'status_change': return '🔄';
      case 'comment': return '💬';
      case 'assignment': return '👤';
      case 'deadline': return '⏰';
      default: return '📝';
    }
  };

  const getTimeAgo = (date: Date) => {
    const now = new Date();
    const diff = now.getTime() - date.getTime();
    const minutes = Math.floor(diff / 60000);
    const hours = Math.floor(diff / 3600000);
    const days = Math.floor(diff / 86400000);

    if (minutes < 1) return 'Just now';
    if (minutes < 60) return `${minutes}m ago`;
    if (hours < 24) return `${hours}h ago`;
    return `${days}d ago`;
  };

  return (
    <div className="bg-white shadow-lg rounded-lg h-96 flex flex-col">
      {/* Header */}
      <div className="border-b border-gray-200 p-4">
        <div className="flex items-center justify-between mb-3">
          <h3 className="text-lg font-semibold text-gray-900">🤝 Collaboration Hub</h3>
          <div className="flex items-center gap-2">
            <div className="flex -space-x-2">
              {teamMembers.slice(0, 3).map((member) => (
                <div
                  key={member.id}
                  className="relative w-8 h-8 bg-gradient-to-r from-primary to-primary-dark rounded-full flex items-center justify-center text-white text-sm font-semibold border-2 border-white"
                  title={member.name}
                >
                  {member.name.charAt(0).toUpperCase()}
                  {activeUsers.includes(member.id) && (
                    <div className="absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white"></div>
                  )}
                </div>
              ))}
              {teamMembers.length > 3 && (
                <div className="w-8 h-8 bg-gray-300 rounded-full flex items-center justify-center text-gray-600 text-xs font-semibold border-2 border-white">
                  +{teamMembers.length - 3}
                </div>
              )}
            </div>
          </div>
        </div>

        {/* Tab Navigation */}
        <nav className="flex space-x-1">
          {[
            { id: 'team', label: '👥 Team', count: teamMembers.length },
            { id: 'activity', label: '📊 Activity', count: activities.length },
            { id: 'tasks', label: '✅ Tasks', count: 0 }
          ].map((tab) => (
            <button
              key={tab.id}
              onClick={() => setSelectedView(tab.id as any)}
              className={`px-3 py-1 text-sm font-medium rounded-md transition-colors ${
                selectedView === tab.id
                  ? 'bg-primary text-white'
                  : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
              }`}
            >
              {tab.label} ({tab.count})
            </button>
          ))}
        </nav>
      </div>

      {/* Content */}
      <div className="flex-1 overflow-y-auto p-4">
        {/* Team View */}
        {selectedView === 'team' && (
          <div className="space-y-3">
            {teamMembers.map((member) => (
              <div key={member.id} className="flex items-center justify-between p-3 border border-gray-200 rounded-lg hover:bg-gray-50">
                <div className="flex items-center gap-3">
                  <div className="relative">
                    <div className="w-10 h-10 bg-gradient-to-r from-primary to-primary-dark rounded-full flex items-center justify-center text-white font-semibold">
                      {member.name.charAt(0).toUpperCase()}
                    </div>
                    {activeUsers.includes(member.id) ? (
                      <div className="absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white" title="Online"></div>
                    ) : (
                      <div className="absolute -bottom-1 -right-1 w-3 h-3 bg-gray-400 rounded-full border-2 border-white" title="Offline"></div>
                    )}
                  </div>
                  <div>
                    <h4 className="font-medium text-gray-900">{member.name}</h4>
                    <p className="text-sm text-gray-600">{member.role}</p>
                    {member.currentTask && (
                      <p className="text-xs text-blue-600">🔄 {member.currentTask}</p>
                    )}
                  </div>
                </div>
                <div className="text-right">
                  <div className={`px-2 py-1 rounded text-xs font-medium ${
                    activeUsers.includes(member.id) 
                      ? 'bg-green-100 text-green-800' 
                      : 'bg-gray-100 text-gray-600'
                  }`}>
                    {activeUsers.includes(member.id) ? 'Online' : getTimeAgo(member.lastSeen)}
                  </div>
                </div>
              </div>
            ))}
          </div>
        )}

        {/* Activity Feed */}
        {selectedView === 'activity' && (
          <div className="space-y-3">
            {activities.map((activity) => (
              <div key={activity.id} className="flex items-start gap-3 p-3 border border-gray-200 rounded-lg">
                <div className="text-2xl">{getActivityIcon(activity.type)}</div>
                <div className="flex-1">
                  <p className="text-sm text-gray-900">{activity.description}</p>
                  <div className="flex items-center gap-2 mt-1">
                    <span className="text-xs text-gray-600">{activity.user.name}</span>
                    <span className="text-xs text-gray-400">•</span>
                    <span className="text-xs text-gray-600">{getTimeAgo(activity.timestamp)}</span>
                  </div>
                </div>
              </div>
            ))}
            {activities.length === 0 && (
              <div className="text-center py-8 text-gray-500">
                <div className="text-4xl mb-2">📝</div>
                <p>No recent activity</p>
              </div>
            )}
          </div>
        )}

        {/* Tasks View */}
        {selectedView === 'tasks' && (
          <div className="space-y-3">
            <div className="text-center py-8 text-gray-500">
              <div className="text-4xl mb-2">✅</div>
              <p>Task management coming soon</p>
              <p className="text-sm">Track assignments, deadlines, and progress</p>
            </div>
          </div>
        )}
      </div>

      {/* Quick Actions Footer */}
      <div className="border-t border-gray-200 p-3">
        <div className="flex gap-2">
          <button className="flex-1 px-3 py-2 text-sm bg-primary text-white rounded-md hover:bg-primary-dark transition-colors">
            💬 Quick Chat
          </button>
          <button className="flex-1 px-3 py-2 text-sm bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 transition-colors">
            📋 Add Task
          </button>
          <button className="px-3 py-2 text-sm bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 transition-colors">
            ⚙️
          </button>
        </div>
      </div>
    </div>
  );
};

export default CollaborationHub; 

CasperSecurity Mini