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/EnhancedDashboard.tsx
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useSession } from 'next-auth/react';
import { formatDistanceToNow } from 'date-fns';
import MessageCenter from './MessageCenter';
import toast from 'react-hot-toast';

interface DashboardStats {
  unreadMessages: number;
  activeCases: number;
  pendingTasks: number;
  upcomingMeetings: number;
}

interface RecentActivity {
  id: string;
  type: 'message' | 'case_update' | 'meeting' | 'document';
  title: string;
  description: string;
  timestamp: string;
  priority?: 'low' | 'medium' | 'high' | 'urgent';
  metadata?: {
    senderId?: string;
    senderName?: string;
    caseId?: string;
    caseTitle?: string;
  };
}

interface EnhancedDashboardProps {
  userRole: string;
  userName: string;
  userId: string;
  children?: React.ReactNode;
}

const EnhancedDashboard: React.FC<EnhancedDashboardProps> = ({
  userRole,
  userName,
  userId,
  children
}) => {
  const { data: session } = useSession();
  const [stats, setStats] = useState<DashboardStats>({
    unreadMessages: 0,
    activeCases: 0,
    pendingTasks: 0,
    upcomingMeetings: 0
  });
  const [recentActivity, setRecentActivity] = useState<RecentActivity[]>([]);
  const [loading, setLoading] = useState(true);
  const [showMessageCenter, setShowMessageCenter] = useState(false);
  const [showProfile, setShowProfile] = useState(false);
  const [selectedProfileUserId, setSelectedProfileUserId] = useState<string | null>(null);

  useEffect(() => {
    fetchDashboardData();
    
    // Set up real-time updates every 30 seconds
    const interval = setInterval(fetchDashboardData, 30000);
    return () => clearInterval(interval);
  }, []);

  const fetchDashboardData = async () => {
    try {
      const [statsResponse, activityResponse] = await Promise.all([
        fetch('/api/dashboard/stats'),
        fetch('/api/dashboard/activity')
      ]);

      if (statsResponse.ok) {
        const statsData = await statsResponse.json();
        setStats(statsData);
      }

      if (activityResponse.ok) {
        const activityData = await activityResponse.json();
        setRecentActivity(activityData.activities || []);
      }
    } catch (error) {
      console.error('Error fetching dashboard data:', error);
    } finally {
      setLoading(false);
    }
  };

  const handleMessageClick = (messageId: string, senderId: string) => {
    // Open team private chat or navigate to conversation
    setShowMessageCenter(false);
    toast.success('Opening conversation...');
    // In real implementation, this would open the chat interface
  };

  const handleProfileClick = (userId: string) => {
    setSelectedProfileUserId(userId);
    setShowProfile(true);
  };

  const handleScheduleMeeting = (userId: string, userName: string) => {
    toast.success(`Opening scheduler for ${userName}`);
    // In real implementation, this would open a scheduling interface
  };

  const handleViewFullProfile = (userId: string) => {
    window.open(`/profile/${userId}`, '_blank');
  };

  const getPriorityIcon = (priority?: string) => {
    switch (priority) {
      case 'urgent': return 'πŸ”΄';
      case 'high': return '🟠';
      case 'medium': return '🟑';
      default: return 'πŸ”΅';
    }
  };

  const getActivityIcon = (type: string) => {
    switch (type) {
      case 'message': return 'πŸ’¬';
      case 'case_update': return 'βš–οΈ';
      case 'meeting': return 'πŸ“…';
      case 'document': return 'πŸ“„';
      default: return 'πŸ“';
    }
  };

  const getRoleGreeting = (role: string) => {
    switch (role) {
      case 'SUPERADMIN':
      case 'SUPERADMIN':
      case 'SUPERADMIN': return 'Legal Representative & CEO Dashboard';
      case 'ADMIN': return 'Legal Team Lead Dashboard';
      case 'LAWYER': return 'Attorney Dashboard';
      case 'SECRETARY': return 'Legal Secretary Dashboard';
      case 'ASSISTANT': return 'Legal Assistant Dashboard';
      case 'CLERK': return 'Legal Clerk Dashboard';
      default: return 'Client Portal';
    }
  };

  const getWelcomeMessage = () => {
    const hour = new Date().getHours();
    let timeGreeting = '';
    
    if (hour < 12) timeGreeting = 'Good morning';
    else if (hour < 17) timeGreeting = 'Good afternoon';
    else timeGreeting = 'Good evening';

    return `${timeGreeting}, ${userName.split(' ')[0]}`;
  };

  return (
    <div className="min-h-screen bg-gray-50 dark:bg-gray-900">
      {/* Enhanced Header */}
      <div className="bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
          <div className="flex items-center justify-between h-16">
            {/* Logo and Title */}
            <div className="flex items-center space-x-4">
              <div className="flex-shrink-0">
                <h1 className="text-xl font-bold text-gray-900 dark:text-white">
                  LibertΓ© MΓͺme En Prison
                </h1>
              </div>
              <div className="hidden md:block">
                <div className="text-sm text-gray-500 dark:text-gray-400">
                  {getRoleGreeting(userRole)}
                </div>
              </div>
            </div>

            {/* Quick Actions */}
            <div className="flex items-center space-x-4">
              {/* Messages Button */}
              <button
                onClick={() => setShowMessageCenter(true)}
                className="relative p-2 text-gray-400 hover:text-gray-500 dark:hover:text-gray-300 transition-colors rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700"
                title="Messages"
              >
                <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-3.582 8-8 8a8.001 8.001 0 01-7.716-6M3 12a9 9 0 1118 0v0z" />
                </svg>
                {stats.unreadMessages > 0 && (
                  <motion.div
                    initial={{ scale: 0 }}
                    animate={{ scale: 1 }}
                    className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center font-medium"
                  >
                    {stats.unreadMessages > 99 ? '99+' : stats.unreadMessages}
                  </motion.div>
                )}
              </button>

              {/* Profile Button */}
              <button
                onClick={() => handleProfileClick(userId)}
                className="flex items-center space-x-2 p-2 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
                title="Your Profile"
              >
                <div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center text-white font-semibold text-sm">
                  {userName.charAt(0).toUpperCase()}
                </div>
                <span className="hidden md:block text-sm font-medium">{userName.split(' ')[0]}</span>
              </button>
            </div>
          </div>
        </div>
      </div>

      {/* Welcome Section */}
      <div className="bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-700 text-white">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
          <div className="flex items-center justify-between">
            <div>
              <h2 className="text-3xl font-bold">{getWelcomeMessage()}</h2>
              <p className="text-blue-100 mt-1">
                Here's what's happening with your legal matters today
              </p>
            </div>
            <div className="hidden lg:block">
              <div className="text-right">
                <div className="text-2xl font-bold">
                  {new Date().toLocaleDateString('en-US', { 
                    weekday: 'long',
                    year: 'numeric',
                    month: 'long',
                    day: 'numeric'
                  })}
                </div>
                <div className="text-blue-200 text-sm">
                  {new Date().toLocaleTimeString('en-US', { 
                    hour: '2-digit',
                    minute: '2-digit'
                  })}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Dashboard Stats */}
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 -mt-4">
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
          {[
            {
              title: 'Unread Messages',
              value: stats.unreadMessages,
              icon: 'πŸ’¬',
              color: 'from-blue-500 to-blue-600',
              action: () => setShowMessageCenter(true),
              urgent: stats.unreadMessages > 0
            },
            {
              title: 'Active Cases',
              value: stats.activeCases,
              icon: 'βš–οΈ',
              color: 'from-purple-500 to-purple-600',
              action: () => {},
              urgent: false
            },
            {
              title: 'Pending Tasks',
              value: stats.pendingTasks,
              icon: 'πŸ“‹',
              color: 'from-green-500 to-green-600',
              action: () => {},
              urgent: stats.pendingTasks > 5
            },
            {
              title: 'Meetings Today',
              value: stats.upcomingMeetings,
              icon: 'πŸ“…',
              color: 'from-orange-500 to-orange-600',
              action: () => {},
              urgent: stats.upcomingMeetings > 0
            }
          ].map((stat, index) => (
            <motion.div
              key={stat.title}
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ delay: index * 0.1 }}
              className={`relative bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden cursor-pointer transition-all duration-200 hover:shadow-xl ${
                stat.urgent ? 'ring-2 ring-red-400 ring-opacity-75' : ''
              }`}
              onClick={stat.action}
            >
              <div className={`absolute inset-0 bg-gradient-to-br ${stat.color} opacity-5`}></div>
              <div className="relative p-6">
                <div className="flex items-center justify-between">
                  <div>
                    <p className="text-sm font-medium text-gray-600 dark:text-gray-400">
                      {stat.title}
                    </p>
                    <p className={`text-3xl font-bold mt-2 ${
                      stat.urgent ? 'text-red-600 dark:text-red-400' : 'text-gray-900 dark:text-white'
                    }`}>
                      {stat.value}
                    </p>
                  </div>
                  <div className="text-4xl opacity-80">
                    {stat.icon}
                  </div>
                </div>
                {stat.urgent && (
                  <div className="mt-3">
                    <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800 animate-pulse">
                      Needs Attention
                    </span>
                  </div>
                )}
              </div>
            </motion.div>
          ))}
        </div>
      </div>

      {/* Main Content Area */}
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-12">
        <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
          {/* Recent Activity */}
          <div className="lg:col-span-2">
            <div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
              <div className="p-6 border-b border-gray-200 dark:border-gray-700">
                <h3 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
                  πŸ“ˆ Recent Activity
                </h3>
                <p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
                  Stay updated with the latest developments
                </p>
              </div>
              <div className="p-6">
                {loading ? (
                  <div className="space-y-4">
                    {[...Array(5)].map((_, i) => (
                      <div key={i} className="animate-pulse flex space-x-4">
                        <div className="rounded-full bg-gray-300 h-10 w-10"></div>
                        <div className="flex-1 space-y-2 py-1">
                          <div className="h-4 bg-gray-300 rounded w-3/4"></div>
                          <div className="h-3 bg-gray-300 rounded w-1/2"></div>
                        </div>
                      </div>
                    ))}
                  </div>
                ) : recentActivity.length === 0 ? (
                  <div className="text-center py-8">
                    <div className="text-5xl mb-4">πŸ“­</div>
                    <p className="text-gray-500 font-medium">No recent activity</p>
                    <p className="text-sm text-gray-400 mt-1">Activity will appear here as it happens</p>
                  </div>
                ) : (
                  <div className="space-y-4">
                    {recentActivity.slice(0, 10).map((activity) => (
                      <motion.div
                        key={activity.id}
                        initial={{ opacity: 0, x: -20 }}
                        animate={{ opacity: 1, x: 0 }}
                        className="flex items-start space-x-4 p-4 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors cursor-pointer"
                        onClick={() => {
                          if (activity.type === 'message' && activity.metadata?.senderId) {
                            handleProfileClick(activity.metadata.senderId);
                          }
                        }}
                      >
                        <div className="flex-shrink-0">
                          <div className="w-10 h-10 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center text-white text-lg">
                            {getActivityIcon(activity.type)}
                          </div>
                        </div>
                        <div className="flex-1 min-w-0">
                          <div className="flex items-center justify-between">
                            <p className="text-sm font-medium text-gray-900 dark:text-white">
                              {activity.title}
                            </p>
                            <div className="flex items-center space-x-2">
                              {activity.priority && (
                                <span className="text-sm">
                                  {getPriorityIcon(activity.priority)}
                                </span>
                              )}
                              <span className="text-xs text-gray-400">
                                {formatDistanceToNow(new Date(activity.timestamp), { addSuffix: true })}
                              </span>
                            </div>
                          </div>
                          <p className="text-sm text-gray-600 dark:text-gray-300 mt-1">
                            {activity.description}
                          </p>
                          {activity.metadata?.caseTitle && (
                            <div className="mt-2">
                              <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">
                                βš–οΈ {activity.metadata.caseTitle}
                              </span>
                            </div>
                          )}
                        </div>
                      </motion.div>
                    ))}
                  </div>
                )}
              </div>
            </div>
          </div>

          {/* Quick Actions & Info */}
          <div className="space-y-6">
            {/* Quick Actions */}
            <div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
              <div className="p-6 border-b border-gray-200 dark:border-gray-700">
                <h3 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
                  ⚑ Quick Actions
                </h3>
              </div>
              <div className="p-6 space-y-3">
                <button
                  onClick={() => setShowMessageCenter(true)}
                  className="w-full flex items-center justify-between p-3 bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300 rounded-lg hover:bg-blue-100 dark:hover:bg-blue-900/30 transition-colors"
                >
                  <span className="flex items-center gap-2">
                    <span>πŸ’¬</span>
                    <span>View Messages</span>
                  </span>
                  {stats.unreadMessages > 0 && (
                    <span className="bg-blue-600 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
                      {stats.unreadMessages}
                    </span>
                  )}
                </button>
                
                <button className="w-full flex items-center gap-2 p-3 bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 rounded-lg hover:bg-green-100 dark:hover:bg-green-900/30 transition-colors">
                  <span>πŸ“…</span>
                  <span>Schedule Meeting</span>
                </button>
                
                <button className="w-full flex items-center gap-2 p-3 bg-purple-50 dark:bg-purple-900/20 text-purple-700 dark:text-purple-300 rounded-lg hover:bg-purple-100 dark:hover:bg-purple-900/30 transition-colors">
                  <span>πŸ“„</span>
                  <span>Upload Document</span>
                </button>
                
                <button className="w-full flex items-center gap-2 p-3 bg-orange-50 dark:bg-orange-900/20 text-orange-700 dark:text-orange-300 rounded-lg hover:bg-orange-100 dark:hover:bg-orange-900/30 transition-colors">
                  <span>βš–οΈ</span>
                  <span>Case Management</span>
                </button>
              </div>
            </div>

            {/* System Status */}
            <div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
              <div className="p-6 border-b border-gray-200 dark:border-gray-700">
                <h3 className="text-lg font-semibold text-gray-900 dark:text-white flex items-center gap-2">
                  🟒 System Status
                </h3>
              </div>
              <div className="p-6 space-y-4">
                <div className="flex items-center justify-between">
                  <span className="text-sm text-gray-600 dark:text-gray-400">Chat System</span>
                  <span className="flex items-center gap-1 text-green-600 text-sm">
                    <div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
                    Online
                  </span>
                </div>
                <div className="flex items-center justify-between">
                  <span className="text-sm text-gray-600 dark:text-gray-400">Case Management</span>
                  <span className="flex items-center gap-1 text-green-600 text-sm">
                    <div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
                    Active
                  </span>
                </div>
                <div className="flex items-center justify-between">
                  <span className="text-sm text-gray-600 dark:text-gray-400">Document Storage</span>
                  <span className="flex items-center gap-1 text-green-600 text-sm">
                    <div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
                    Operational
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Original Dashboard Content */}
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        {children}
      </div>

      {/* Message Center Modal */}
      <MessageCenter
        isOpen={showMessageCenter}
        onClose={() => setShowMessageCenter(false)}
        onMessageClick={handleMessageClick}
      />

      {/* Modern Profile Modal - Component removed */}
      {selectedProfileUserId && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
          <div className="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4">
            <div className="flex items-center justify-between mb-4">
              <h3 className="text-lg font-semibold text-gray-900 dark:text-white">User Profile</h3>
              <button
                onClick={() => {
                  setShowProfile(false);
                  setSelectedProfileUserId(null);
                }}
                className="text-gray-400 hover:text-gray-600"
              >
                βœ•
              </button>
            </div>
            <p className="text-gray-600 dark:text-gray-400">Profile view coming soon...</p>
          </div>
        </div>
      )}
    </div>
  );
};

export default EnhancedDashboard; 

CasperSecurity Mini