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/SocietyDegreeTracker.tsx
import React, { useState, useEffect } from 'react';
import { format } from 'date-fns';
import { FiStar, FiLock, FiUnlock, FiAward, FiEye, FiEyeOff, FiClock, FiTrendingUp, FiUsers, FiBookOpen } from 'react-icons/fi';

interface SocietyDegreeTrackerProps {
  userId: string;
  showProgress?: boolean;
  showCeremonies?: boolean;
}

interface UserDegree {
  id: string;
  achievedAt: string;
  ceremonyCompleted: boolean;
  ceremonyDate?: string;
  progressPercentage: number;
  isCurrentTarget: boolean;
  degree: {
    id: string;
    degreeNumber: number;
    name: string;
    title?: string;
    description: string;
    xpRequired: number;
    symbol?: string;
    color?: string;
    lodgeLevel: string;
    isSecret: boolean;
    privileges: string[];
    requirements: {
      xp: number;
      cases: number;
      clients: number;
      proBono: number;
      mentorship: number;
      winRate?: number;
      timeRequirement?: number;
    };
  };
}

interface NextDegree {
  id: string;
  degreeNumber: number;
  name: string;
  title?: string;
  description: string;
  xpRequired: number;
  symbol?: string;
  color?: string;
  lodgeLevel: string;
  isSecret: boolean;
  requirements: {
    xp: number;
    cases: number;
    clients: number;
    proBono: number;
    mentorship: number;
    winRate?: number;
    timeRequirement?: number;
  };
  progressPercentage: number;
}

interface UserStats {
  currentXP: number;
  totalCases: number;
  totalClients: number;
  proBonoHours: number;
  mentorshipSessions: number;
  winRate: number;
}

interface DegreeProgressData {
  currentDegree?: UserDegree;
  nextDegree?: NextDegree;
  allDegrees: UserDegree[];
  userStats: UserStats;
  lodgeMemberships: Array<{
    lodge: {
      name: string;
      lodgeLevel: string;
      description: string;
    };
    role: string;
    joinedDate: string;
  }>;
}

const SocietyDegreeTracker: React.FC<SocietyDegreeTrackerProps> = ({ 
  userId, 
  showProgress = true, 
  showCeremonies = true 
}) => {
  const [data, setData] = useState<DegreeProgressData | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [activeTab, setActiveTab] = useState<'overview' | 'degrees' | 'lodges' | 'ceremonies'>('overview');

  useEffect(() => {
    const fetchDegreeProgress = async () => {
      try {
        setLoading(true);
        const response = await fetch(`/api/user/${userId}/society-progress`);
        if (!response.ok) {
          throw new Error('Failed to fetch degree progress');
        }
        const progressData = await response.json();
        setData(progressData);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'An error occurred');
      } finally {
        setLoading(false);
      }
    };

    if (userId) {
      fetchDegreeProgress();
    }
  }, [userId]);

  if (loading) {
    return (
      <div className="flex items-center justify-center p-8">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="text-center p-8">
        <p className="text-red-600">Error loading degree progress: {error}</p>
      </div>
    );
  }

  if (!data) {
    return (
      <div className="text-center p-8">
        <p className="text-gray-600">No degree data available</p>
      </div>
    );
  }

  const { currentDegree, nextDegree, allDegrees, userStats, lodgeMemberships } = data;

  const getLodgeLevelColor = (level: string) => {
    switch (level) {
      case 'BLUE': return 'text-blue-600 bg-blue-100';
      case 'RED': return 'text-red-600 bg-red-100';
      case 'BLACK': return 'text-gray-800 bg-gray-200';
      default: return 'text-gray-600 bg-gray-100';
    }
  };

  const formatRequirement = (key: string, value: number) => {
    const labels: Record<string, string> = {
      xp: 'XP Points',
      cases: 'Cases',
      clients: 'Clients',
      proBono: 'Pro Bono Hours',
      mentorship: 'Mentorship Sessions',
      winRate: 'Win Rate %',
      timeRequirement: 'Days Required'
    };
    return `${labels[key] || key}: ${value}`;
  };

  const calculateProgress = (requirement: number, current: number) => {
    return Math.min((current / requirement) * 100, 100);
  };

  return (
    <div className="bg-white rounded-lg shadow-lg p-6">
      {/* Header */}
      <div className="mb-6">
        <h2 className="text-2xl font-bold text-gray-900 mb-2">Society of Brothers Progress</h2>
        <p className="text-gray-600">Your journey through the 33 degrees of legal mastery</p>
      </div>

      {/* Navigation Tabs */}
      <div className="border-b border-gray-200 mb-6">
        <nav className="-mb-px flex space-x-8">
          {[
            { key: 'overview', label: 'Overview', icon: FiTrendingUp },
            { key: 'degrees', label: 'Degrees', icon: FiAward },
            { key: 'lodges', label: 'Lodges', icon: FiUsers },
            { key: 'ceremonies', label: 'Ceremonies', icon: FiBookOpen }
          ].map(({ key, label, icon: Icon }) => (
            <button
              key={key}
              onClick={() => setActiveTab(key as any)}
              className={`py-2 px-1 border-b-2 font-medium text-sm flex items-center space-x-2 ${
                activeTab === key
                  ? 'border-blue-500 text-blue-600'
                  : 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'
              }`}
            >
              <Icon className="w-4 h-4" />
              <span>{label}</span>
            </button>
          ))}
        </nav>
      </div>

      {/* Overview Tab */}
      {activeTab === 'overview' && (
        <div className="space-y-6">
          {/* Current Degree */}
          {currentDegree && (
            <div className="bg-gradient-to-r from-blue-50 to-purple-50 p-6 rounded-lg">
              <div className="flex items-center justify-between mb-4">
                <div className="flex items-center space-x-3">
                  <div className="text-4xl">{currentDegree.degree.symbol || '🎖️'}</div>
                  <div>
                    <h3 className="text-xl font-bold text-gray-900">
                      {currentDegree.degree.degreeNumber}° {currentDegree.degree.name}
                    </h3>
                    {currentDegree.degree.title && (
                      <p className="text-purple-600 font-medium">{currentDegree.degree.title}</p>
                    )}
                    <span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${getLodgeLevelColor(currentDegree.degree.lodgeLevel)}`}>
                      {currentDegree.degree.lodgeLevel} Lodge
                      {currentDegree.degree.isSecret && <FiEyeOff className="ml-1 w-3 h-3" />}
                    </span>
                  </div>
                </div>
                <div className="text-right">
                  <p className="text-sm text-gray-600">Achieved</p>
                  <p className="font-medium">{format(new Date(currentDegree.achievedAt), 'MMM dd, yyyy')}</p>
                  {currentDegree.ceremonyCompleted && (
                    <div className="flex items-center text-green-600 text-sm mt-1">
                      <FiAward className="w-3 h-3 mr-1" />
                      Ceremony Complete
                    </div>
                  )}
                </div>
              </div>
              <p className="text-gray-700 mb-4">{currentDegree.degree.description}</p>
              
              {/* Privileges */}
              <div>
                <h4 className="font-medium text-gray-900 mb-2">Privileges Unlocked:</h4>
                <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
                  {currentDegree.degree.privileges.map((privilege, index) => (
                    <div key={index} className="flex items-center text-sm text-green-700">
                      <FiUnlock className="w-3 h-3 mr-2" />
                      {privilege}
                    </div>
                  ))}
                </div>
              </div>
            </div>
          )}

          {/* Next Degree Progress */}
          {nextDegree && showProgress && (
            <div className="bg-gray-50 p-6 rounded-lg">
              <div className="flex items-center justify-between mb-4">
                <div className="flex items-center space-x-3">
                  <div className="text-3xl opacity-50">{nextDegree.symbol || '🔒'}</div>
                  <div>
                    <h3 className="text-lg font-bold text-gray-900">
                      Next: {nextDegree.degreeNumber}° {nextDegree.name}
                    </h3>
                    {nextDegree.title && (
                      <p className="text-gray-600">{nextDegree.title}</p>
                    )}
                  </div>
                </div>
                <div className="text-right">
                  <div className="text-2xl font-bold text-blue-600">{nextDegree.progressPercentage.toFixed(1)}%</div>
                  <p className="text-sm text-gray-600">Complete</p>
                </div>
              </div>

              {/* Progress Bars */}
              <div className="space-y-3">
                {Object.entries(nextDegree.requirements).map(([key, required]) => {
                  if (key === 'timeRequirement' || required === 0) return null;
                  
                  let current = 0;
                  switch (key) {
                    case 'xp': current = userStats.currentXP; break;
                    case 'cases': current = userStats.totalCases; break;
                    case 'clients': current = userStats.totalClients; break;
                    case 'proBono': current = userStats.proBonoHours; break;
                    case 'mentorship': current = userStats.mentorshipSessions; break;
                    case 'winRate': current = userStats.winRate; break;
                  }
                  
                  const progress = calculateProgress(required, current);
                  
                  return (
                    <div key={key}>
                      <div className="flex justify-between text-sm text-gray-600 mb-1">
                        <span>{formatRequirement(key, required)}</span>
                        <span>{current} / {required}</span>
                      </div>
                      <div className="w-full bg-gray-200 rounded-full h-2">
                        <div 
                          className={`h-2 rounded-full transition-all duration-300 ${
                            progress >= 100 ? 'bg-green-500' : 'bg-blue-500'
                          }`}
                          style={{ width: `${Math.min(progress, 100)}%` }}
                        ></div>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          {/* User Statistics */}
          <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
            <div className="bg-blue-50 p-4 rounded-lg text-center">
              <div className="text-2xl font-bold text-blue-600">{userStats.currentXP.toLocaleString()}</div>
              <div className="text-sm text-gray-600">XP Points</div>
            </div>
            <div className="bg-green-50 p-4 rounded-lg text-center">
              <div className="text-2xl font-bold text-green-600">{userStats.totalCases}</div>
              <div className="text-sm text-gray-600">Total Cases</div>
            </div>
            <div className="bg-purple-50 p-4 rounded-lg text-center">
              <div className="text-2xl font-bold text-purple-600">{userStats.proBonoHours}</div>
              <div className="text-sm text-gray-600">Pro Bono Hours</div>
            </div>
            <div className="bg-yellow-50 p-4 rounded-lg text-center">
              <div className="text-2xl font-bold text-yellow-600">
                {typeof userStats.winRate === 'number' ? userStats.winRate.toFixed(1) + '%' : 'N/A'}
              </div>
              <div className="text-sm text-gray-600">Win Rate</div>
            </div>
          </div>
        </div>
      )}

      {/* Degrees Tab */}
      {activeTab === 'degrees' && (
        <div className="space-y-4">
          {allDegrees.length === 0 ? (
            <p className="text-gray-600 text-center py-8">No degrees achieved yet</p>
          ) : (
            allDegrees.map((userDegree) => (
              <div key={userDegree.id} className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
                <div className="flex items-center justify-between">
                  <div className="flex items-center space-x-3">
                    <div className="text-2xl">{userDegree.degree.symbol || '🎖️'}</div>
                    <div>
                      <h3 className="font-semibold text-gray-900">
                        {userDegree.degree.degreeNumber}° {userDegree.degree.name}
                      </h3>
                      {userDegree.degree.title && (
                        <p className="text-sm" style={{ color: userDegree.degree.color }}>{userDegree.degree.title}</p>
                      )}
                      <span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${getLodgeLevelColor(userDegree.degree.lodgeLevel)}`}>
                        {userDegree.degree.lodgeLevel} Lodge
                        {userDegree.degree.isSecret && <FiEyeOff className="ml-1 w-3 h-3" />}
                      </span>
                    </div>
                  </div>
                  <div className="text-right">
                    <p className="text-sm text-gray-600">Achieved</p>
                    <p className="font-medium">{format(new Date(userDegree.achievedAt), 'MMM dd, yyyy')}</p>
                    {userDegree.ceremonyCompleted ? (
                      <div className="flex items-center text-green-600 text-sm mt-1">
                        <FiAward className="w-3 h-3 mr-1" />
                        Ceremony Complete
                      </div>
                    ) : (
                      <div className="flex items-center text-orange-600 text-sm mt-1">
                        <FiClock className="w-3 h-3 mr-1" />
                        Awaiting Ceremony
                      </div>
                    )}
                  </div>
                </div>
                <p className="text-gray-600 mt-2">{userDegree.degree.description}</p>
              </div>
            ))
          )}
        </div>
      )}

      {/* Lodges Tab */}
      {activeTab === 'lodges' && (
        <div className="space-y-4">
          {lodgeMemberships.length === 0 ? (
            <p className="text-gray-600 text-center py-8">No lodge memberships</p>
          ) : (
            lodgeMemberships.map((membership, index) => (
              <div key={index} className="border border-gray-200 rounded-lg p-4">
                <div className="flex items-center justify-between mb-3">
                  <div>
                    <h3 className="font-semibold text-gray-900">{membership.lodge.name}</h3>
                    <span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${getLodgeLevelColor(membership.lodge.lodgeLevel)}`}>
                      {membership.lodge.lodgeLevel} Lodge
                    </span>
                  </div>
                  <div className="text-right">
                    <p className="font-medium text-blue-600">{membership.role}</p>
                    <p className="text-sm text-gray-600">Since {format(new Date(membership.joinedDate), 'MMM yyyy')}</p>
                  </div>
                </div>
                <p className="text-gray-600">{membership.lodge.description}</p>
              </div>
            ))
          )}
        </div>
      )}

      {/* Ceremonies Tab */}
      {activeTab === 'ceremonies' && showCeremonies && (
        <div className="space-y-4">
          {allDegrees.filter(d => d.ceremonyCompleted).length === 0 ? (
            <p className="text-gray-600 text-center py-8">No ceremonies completed yet</p>
          ) : (
            allDegrees
              .filter(d => d.ceremonyCompleted)
              .map((userDegree) => (
                <div key={userDegree.id} className="border border-gray-200 rounded-lg p-4 bg-gradient-to-r from-yellow-50 to-amber-50">
                  <div className="flex items-center space-x-3 mb-3">
                    <div className="text-2xl">{userDegree.degree.symbol || '🎖️'}</div>
                    <div>
                      <h3 className="font-semibold text-gray-900">
                        {userDegree.degree.degreeNumber}° {userDegree.degree.name} Ceremony
                      </h3>
                      {userDegree.ceremonyDate && (
                        <p className="text-sm text-gray-600">
                          Conducted on {format(new Date(userDegree.ceremonyDate), 'MMMM dd, yyyy')}
                        </p>
                      )}
                    </div>
                  </div>
                  <div className="bg-white p-3 rounded border-l-4 border-yellow-500">
                    <p className="text-gray-700 italic">
                      "By the working tools of an Entered Apprentice, you are received into this ancient and honorable society of legal practitioners."
                    </p>
                  </div>
                </div>
              ))
          )}
        </div>
      )}
    </div>
  );
};

export default SocietyDegreeTracker;

CasperSecurity Mini