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/pages/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/pages/business-profiles.tsx
import React, { useState, useEffect } from 'react';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
import LayoutWithSidebar from '../components/LayoutWithSidebar';
import { motion } from 'framer-motion';
import { 
  Building2, Users, Award, TrendingUp, MapPin, 
  Phone, Mail, Globe, Star, Target, Shield,
  Calendar, DollarSign, Clock, Filter, Search
} from 'lucide-react';
import Link from 'next/link';
import HireLawyerButton from '../components/HireLawyerButton';
import { prisma } from '../lib/prisma';
import BarreauBadge from '../components/BarreauBadge';

interface LawyerStats {
  totalCases: number;
  wonCases: number;
  lostCases: number;
  winRate: number;
  averageRating: number;
  yearsOfExperience: number;
  hourlyRate: number;
  totalBadges: number;
  level: number;
}

interface BusinessProfile {
  id: string;
  businessName: string;
  businessType: string;
  industry?: string;
  description?: string;
  logo?: string;
  website?: string;
  phone?: string;
  email?: string;
  address?: string;
  employeeCount?: string;
  annualRevenue?: string;
  isVerified: boolean;
  createdAt: string;
  updatedAt: string;
  user: {
    id: string;
    name: string;
    email: string;
    role: string;
  };
  lawyers: Array<{
    id: string;
    name: string;
    role: string;
    profilePicture?: string;
    specialization?: string;
    yearsOfExperience?: number;
    totalCases: number;
    wonCases: number;
    lostCases: number;
    winRate: number;
    averageRating: number;
    hourlyRate?: number;
    totalBadges: number;
    level: number;
    isVerified: boolean;
    verificationStatus?: string;
  }>;
  firmStats: {
    totalLawyers: number;
    totalCases: number;
    totalWonCases: number;
    totalLostCases: number;
    averageWinRate: number;
    averageRating: number;
    averageHourlyRate: number;
    totalBadges: number;
    averageLevel: number;
  };
}

interface BusinessProfilesPageProps {
  businesses: BusinessProfile[];
}

const BusinessProfilesPage: React.FC<BusinessProfilesPageProps> = ({ businesses }) => {
  const [filteredBusinesses, setFilteredBusinesses] = useState<BusinessProfile[]>(businesses);
  const [searchTerm, setSearchTerm] = useState('');
  const [sortBy, setSortBy] = useState('name');
  const [filterVerified, setFilterVerified] = useState(false);

  useEffect(() => {
    let filtered = businesses;

    // Search filter
    if (searchTerm) {
      filtered = filtered.filter(business =>
        business.businessName.toLowerCase().includes(searchTerm.toLowerCase()) ||
        business.industry?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        business.description?.toLowerCase().includes(searchTerm.toLowerCase())
      );
    }

    // Verified filter
    if (filterVerified) {
      filtered = filtered.filter(business => business.isVerified);
    }

    // Sort
    filtered.sort((a, b) => {
      switch (sortBy) {
        case 'name':
          return a.businessName.localeCompare(b.businessName);
        case 'winRate':
          return b.firmStats.averageWinRate - a.firmStats.averageWinRate;
        case 'rating':
          return (b.firmStats.averageRating || 0) - (a.firmStats.averageRating || 0);
        case 'cases':
          return b.firmStats.totalCases - a.firmStats.totalCases;
        case 'lawyers':
          return b.firmStats.totalLawyers - a.firmStats.totalLawyers;
        default:
          return 0;
      }
    });

    setFilteredBusinesses(filtered);
  }, [businesses, searchTerm, sortBy, filterVerified]);

  const getWinRateColor = (winRate: number) => {
    if (winRate >= 80) return 'text-green-600';
    if (winRate >= 60) return 'text-yellow-600';
    return 'text-red-600';
  };

  const getRatingColor = (rating: number) => {
    if (rating >= 4.5) return 'text-yellow-500';
    if (rating >= 4.0) return 'text-gray-600';
    return 'text-gray-400';
  };

  return (
    <LayoutWithSidebar>
      <Head>
        <title>Business Profiles - Legal Firms & Lawyers</title>
        <meta name="description" content="Discover verified legal firms and their expert lawyers with detailed statistics, win rates, and client reviews." />
      </Head>

      <div className="max-w-7xl mx-auto px-4 py-8">
        {/* Quick Navigation */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.1 }}
          className="bg-gradient-to-r from-blue-50 to-purple-50 dark:from-blue-900/20 dark:to-purple-900/20 rounded-xl p-6 border border-blue-200 dark:border-blue-800 mb-8"
        >
          <h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4 flex items-center">
            🏛️ Judicial Ecosystem Navigation
          </h2>
          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
            <Link
              href="/judicial-directory"
              className="flex items-center space-x-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm hover:shadow-md transition-all duration-200 group"
            >
              <div className="w-10 h-10 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center group-hover:scale-110 transition-transform">
                <span className="text-blue-600 text-lg">⚖️</span>
              </div>
              <div>
                <div className="font-medium text-gray-900 dark:text-white group-hover:text-blue-600 transition-colors">
                  Judicial Directory
                </div>
                <div className="text-sm text-gray-500 dark:text-gray-400">
                  Legal professionals & expertise
                </div>
              </div>
            </Link>
            <Link
              href="/business-profiles"
              className="flex items-center space-x-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm hover:shadow-md transition-all duration-200 group border-2 border-blue-500"
            >
              <div className="w-10 h-10 bg-purple-100 dark:bg-purple-900/30 rounded-lg flex items-center justify-center group-hover:scale-110 transition-transform">
                <span className="text-purple-600 text-lg">🏢</span>
              </div>
              <div>
                <div className="font-medium text-gray-900 dark:text-white group-hover:text-purple-600 transition-colors">
                  Law Firms & Businesses
                </div>
                <div className="text-sm text-gray-500 dark:text-gray-400">
                  Professional organizations
                </div>
              </div>
            </Link>
            <Link
              href="/profiles"
              className="flex items-center space-x-3 p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm hover:shadow-md transition-all duration-200 group"
            >
              <div className="w-10 h-10 bg-green-100 dark:bg-green-900/30 rounded-lg flex items-center justify-center group-hover:scale-110 transition-transform">
                <span className="text-green-600 text-lg">👥</span>
              </div>
              <div>
                <div className="font-medium text-gray-900 dark:text-white group-hover:text-green-600 transition-colors">
                  Society Members
                </div>
                <div className="text-sm text-gray-500 dark:text-gray-400">
                  Community directory
                </div>
              </div>
            </Link>
          </div>
        </motion.div>

        {/* Header */}
        <div className="mb-8">
          <h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">
            ⚖️ Law Firms Directory
          </h1>
          <p className="text-xl text-gray-600 dark:text-gray-400">
            Discover verified law firms and legal organizations with detailed statistics, 
            team information, and performance metrics in our judicial ecosystem
          </p>
        </div>

        {/* Filters */}
        <div className="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6 mb-8">
          <div className="flex flex-col md:flex-row gap-4">
            {/* Search */}
            <div className="flex-1">
              <div className="relative">
                <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-gray-400" />
                <input
                  type="text"
                  placeholder="Search firms, lawyers, or specializations..."
                  value={searchTerm}
                  onChange={(e) => setSearchTerm(e.target.value)}
                  className="w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white"
                />
              </div>
            </div>

            {/* Sort */}
            <div className="flex gap-4">
              <select
                value={sortBy}
                onChange={(e) => setSortBy(e.target.value)}
                className="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-white"
              >
                <option value="name">Sort by Name</option>
                <option value="winRate">Sort by Win Rate</option>
                <option value="rating">Sort by Rating</option>
                <option value="cases">Sort by Total Cases</option>
                <option value="lawyers">Sort by Lawyers</option>
              </select>

              <label className="flex items-center">
                <input
                  type="checkbox"
                  checked={filterVerified}
                  onChange={(e) => setFilterVerified(e.target.checked)}
                  className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
                />
                <span className="ml-2 text-sm text-gray-700 dark:text-gray-300">Verified Only</span>
              </label>
            </div>
          </div>
        </div>

        {/* Results */}
        <div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
          {filteredBusinesses.map((business, index) => (
            <motion.div
              key={business.id}
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ delay: index * 0.1 }}
              whileHover={{ y: -5, scale: 1.02 }}
              className="bg-white dark:bg-gray-800 rounded-xl shadow-lg hover:shadow-2xl transition-all duration-300 overflow-hidden border border-gray-100 dark:border-gray-700 cursor-pointer group"
              onClick={() => window.open(`/business/${business.id}`, '_blank')}
            >
              <div className="p-6">
                {/* Header */}
                <div className="flex items-start justify-between mb-4">
                  <div className="flex items-center">
                    <div className="relative">
                      {business.logo ? (
                        <img
                          src={business.logo}
                          alt={business.businessName}
                          className="w-16 h-16 rounded-lg object-cover mr-4 group-hover:scale-110 transition-transform"
                        />
                      ) : (
                        <div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center mr-4 group-hover:scale-110 transition-transform">
                          <Building2 className="h-8 w-8 text-white" />
                        </div>
                      )}
                      {business.isVerified && (
                        <div className="absolute -top-1 -right-1 w-6 h-6 bg-blue-600 rounded-full flex items-center justify-center">
                          <Shield className="h-3 w-3 text-white" />
                        </div>
                      )}
                    </div>
                    <div>
                      <h3 className="font-semibold text-lg text-gray-900 dark:text-white flex items-center group-hover:text-blue-600 transition-colors">
                        {business.businessName}
                        {business.isVerified && (
                          <Shield className="h-5 w-5 ml-2 text-blue-600" />
                        )}
                      </h3>
                      <p className="text-sm text-gray-600 dark:text-gray-400">{business.businessType}</p>
                      {business.industry && (
                        <p className="text-xs text-gray-500">{business.industry}</p>
                      )}
                    </div>
                  </div>
                  <div className="opacity-0 group-hover:opacity-100 transition-opacity">
                    <span className="text-blue-600 text-sm font-medium">🏢 View Firm</span>
                  </div>
                </div>

                {/* Firm Stats */}
                <div className="grid grid-cols-2 gap-4 mb-4">
                  <div className="text-center p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg group-hover:bg-blue-100 dark:group-hover:bg-blue-900/30 transition-colors">
                    <div className="text-2xl font-bold text-blue-600">
                      {business.firmStats.totalLawyers > 0 ? business.firmStats.totalLawyers : <span className="text-gray-400 text-base">No lawyers yet</span>}
                    </div>
                    <div className="text-xs text-gray-600 dark:text-gray-400">Lawyers</div>
                  </div>
                  <div className="text-center p-3 bg-green-50 dark:bg-green-900/20 rounded-lg group-hover:bg-green-100 dark:group-hover:bg-green-900/30 transition-colors">
                    <div className={`text-2xl font-bold ${getWinRateColor(business.firmStats.averageWinRate)}`}> 
                      {business.firmStats.totalCases > 0 && typeof business.firmStats.averageWinRate === 'number' ? business.firmStats.averageWinRate.toFixed(1) + '%' : <span className="text-gray-400 text-base">N/A</span>}
                    </div>
                    <div className="text-xs text-gray-600 dark:text-gray-400">Win Rate</div>
                  </div>
                  <div className="text-center p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg group-hover:bg-purple-100 dark:group-hover:bg-purple-900/30 transition-colors">
                    <div className="text-2xl font-bold text-purple-600">
                      {business.firmStats.totalCases > 0 ? business.firmStats.totalCases : <span className="text-gray-400 text-base">No cases yet</span>}
                    </div>
                    <div className="text-xs text-gray-600 dark:text-gray-400">Total Cases</div>
                  </div>
                  <div className="text-center p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg group-hover:bg-yellow-100 dark:group-hover:bg-yellow-900/30 transition-colors">
                    <div className={`text-2xl font-bold ${getRatingColor(business.firmStats.averageRating || 0)}`}> 
                      {business.firmStats.averageRating && business.firmStats.averageRating > 0 ? business.firmStats.averageRating.toFixed(1) : <span className="text-gray-400 text-base">N/A</span>}
                    </div>
                    <div className="text-xs text-gray-600 dark:text-gray-400">Rating</div>
                  </div>
                </div>

                {/* Description */}
                {business.description && (
                  <p className="text-sm text-gray-600 dark:text-gray-400 mb-4 line-clamp-2">
                    {business.description}
                  </p>
                )}

                {/* Contact Info */}
                <div className="space-y-2 mb-4">
                  {business.website && (
                    <div className="flex items-center text-sm text-gray-600 dark:text-gray-400">
                      <Globe className="h-4 w-4 mr-2" />
                      <span className="truncate">{business.website}</span>
                    </div>
                  )}
                  {business.phone && (
                    <div className="flex items-center text-sm text-gray-600 dark:text-gray-400">
                      <Phone className="h-4 w-4 mr-2" />
                      {business.phone}
                    </div>
                  )}
                  {business.address && (
                    <div className="flex items-center text-sm text-gray-600 dark:text-gray-400">
                      <MapPin className="h-4 w-4 mr-2" />
                      <span className="truncate">{business.address}</span>
                    </div>
                  )}
                </div>

                {/* Top Lawyers Preview */}
                {business.lawyers.length > 0 ? (
                  <div className="border-t border-gray-200 dark:border-gray-700 pt-4">
                    <h4 className="text-sm font-medium text-gray-900 dark:text-white mb-2">Top Lawyers</h4>
                    <div className="space-y-2">
                      {business.lawyers.slice(0, 3).map((lawyer) => (
                        <div key={lawyer.id} className="flex items-center justify-between group/lawyer">
                          <div className="flex items-center">
                            {lawyer.profilePicture ? (
                              <img
                                src={lawyer.profilePicture}
                                alt={lawyer.name}
                                className="w-8 h-8 rounded-full object-cover mr-2 group-hover/lawyer:scale-110 transition-transform"
                              />
                            ) : (
                              <div className="w-8 h-8 bg-gray-300 dark:bg-gray-600 rounded-full flex items-center justify-center mr-2 group-hover/lawyer:scale-110 transition-transform">
                                <Users className="h-4 w-4 text-gray-500" />
                              </div>
                            )}
                            <div>
                              <div className="flex items-center gap-2 mb-2">
                                <span className="text-sm font-medium text-gray-900">{lawyer.name}</span>
                                <BarreauBadge 
                                  verificationStatus={lawyer.verificationStatus}
                                  isVerified={lawyer.isVerified}
                                  size="sm"
                                  showText={false}
                                />
                              </div>
                              <p className="text-xs text-gray-600 dark:text-gray-400">{lawyer.specialization}</p>
                            </div>
                          </div>
                          <div className="text-right">
                            <p className={`text-sm font-medium ${getWinRateColor(lawyer.winRate)}`}>
                              {typeof lawyer.winRate === 'number' ? lawyer.winRate.toFixed(1) + '%' : 'N/A'}
                            </p>
                            <p className="text-xs text-gray-600 dark:text-gray-400">{lawyer.totalCases} cases</p>
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                ) : (
                  <div className="border-t border-gray-200 dark:border-gray-700 pt-4 text-center text-gray-400 text-sm">
                    No lawyers yet
                  </div>
                )}

                {/* Hire Button */}
                {business.lawyers.length > 0 && (
                  <div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
                    <HireLawyerButton 
                      lawyer={business.lawyers[0]} 
                      businessProfile={business}
                      className="w-full"
                    />
                  </div>
                )}

                {/* Quick Action Buttons */}
                <div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700 flex gap-2">
                  <button
                    onClick={(e) => {
                      e.stopPropagation();
                      window.open(`/business/${business.id}`, '_blank');
                    }}
                    className="flex-1 bg-gradient-to-r from-blue-500 to-purple-600 text-white px-3 py-2 rounded-md text-sm font-medium hover:from-blue-600 hover:to-purple-700 transition-all"
                  >
                    🏢 View Firm Profile
                  </button>
                  <button
                    onClick={(e) => {
                      e.stopPropagation();
                      window.open(`/hire?firm=${business.id}`, '_blank');
                    }}
                    className="bg-green-600 text-white px-3 py-2 rounded-md text-sm font-medium hover:bg-green-700 transition-colors"
                  >
                    💼 Hire Firm
                  </button>
                </div>
              </div>
            </motion.div>
          ))}
        </div>

        {/* Empty State */}
        {filteredBusinesses.length === 0 && (
          <div className="text-center py-12">
            <Building2 className="h-16 w-16 text-gray-400 mx-auto mb-4" />
            <h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">No businesses found</h3>
            <p className="text-gray-600 dark:text-gray-400">
              Try adjusting your search criteria or filters.
            </p>
          </div>
        )}
      </div>
    </LayoutWithSidebar>
  );
};

function serializeDates(obj: any): any {
  if (obj === null || obj === undefined) return obj;
  if (obj instanceof Date) return obj.toISOString();
  if (Array.isArray(obj)) return obj.map(serializeDates);
  if (typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj).map(([k, v]) => [k, serializeDates(v)])
    );
  }
  return obj;
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  let businesses = [];
  if (process.env.NODE_ENV === 'development') {
    // Direct DB call in dev to avoid self-signed cert issues
    const businessProfiles = await prisma.businessProfile.findMany({
      where: { isPublic: true },
      include: {
        owner: {
          select: {
            id: true,
            name: true,
            email: true,
            role: true,
            isProfilePublic: true,
            profilePicture: true,
            specialization: true,
            yearsOfExperience: true,
            totalCases: true,
            wonCases: true,
            lostCases: true,
            averageRating: true,
            hourlyRate: true,
            totalBadges: true,
            level: true,
            isVerified: true,
            verificationStatus: true
          }
        },
        members: {
          select: {
            id: true,
            name: true,
            email: true,
            role: true,
            profilePicture: true,
            specialization: true,
            yearsOfExperience: true,
            totalCases: true,
            wonCases: true,
            lostCases: true,
            averageRating: true,
            hourlyRate: true,
            totalBadges: true,
            level: true,
            isVerified: true,
            verificationStatus: true,
            isProfilePublic: true
          }
        }
      },
      orderBy: { createdAt: 'desc' }
    });
    businesses = businessProfiles.map((business) => {
      const allLawyers = [business.owner, ...business.members]
        .filter(lawyer => lawyer.isProfilePublic)
        .filter((lawyer, index, self) => 
          index === self.findIndex(l => l.id === lawyer.id)
        );
      const lawyersWithStats = allLawyers.map(lawyer => ({
        ...lawyer,
        winRate: lawyer.totalCases > 0 ? (lawyer.wonCases / lawyer.totalCases) * 100 : 0
      }));
      const firmStats = {
        totalLawyers: lawyersWithStats.length,
        totalCases: lawyersWithStats.reduce((sum, lawyer) => sum + lawyer.totalCases, 0),
        totalWonCases: lawyersWithStats.reduce((sum, lawyer) => sum + lawyer.wonCases, 0),
        totalLostCases: lawyersWithStats.reduce((sum, lawyer) => sum + lawyer.lostCases, 0),
        averageWinRate: lawyersWithStats.length > 0 ? lawyersWithStats.reduce((sum, lawyer) => sum + lawyer.winRate, 0) / lawyersWithStats.length : 0,
        averageRating: lawyersWithStats.length > 0 ? lawyersWithStats.reduce((sum, lawyer) => sum + (lawyer.averageRating || 0), 0) / lawyersWithStats.length : 0,
        averageHourlyRate: lawyersWithStats.length > 0 ? lawyersWithStats.reduce((sum, lawyer) => sum + (lawyer.hourlyRate || 0), 0) / lawyersWithStats.length : 0,
        totalBadges: lawyersWithStats.reduce((sum, lawyer) => sum + lawyer.totalBadges, 0),
        averageLevel: lawyersWithStats.length > 0 ? lawyersWithStats.reduce((sum, lawyer) => sum + lawyer.level, 0) / lawyersWithStats.length : 0
      };
      return { ...business, lawyers: lawyersWithStats, firmStats };
    });
  } else {
    try {
      const response = await fetch(`${process.env.NEXTAUTH_URL || 'http://localhost:3000'}/api/public/business-profiles`);
      businesses = await response.json();
    } catch (error) {
      businesses = [];
    }
  }
  return { props: { businesses: serializeDates(businesses) } };
};

export default BusinessProfilesPage; 

CasperSecurity Mini