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/backups/lavocat.quebec/backup-20250730-021618/src/pages/judge/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/backups/lavocat.quebec/backup-20250730-021618/src/pages/judge/opinions.tsx
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import { useRequireRole } from '../../lib/auth-utils';
import { USER_ROLES } from '../../lib/auth-utils';

interface LegalOpinion {
  id: string;
  title: string;
  caseNumber: string;
  caseTitle: string;
  opinionType: 'majority' | 'concurring' | 'dissenting' | 'per_curiam';
  status: 'draft' | 'review' | 'published' | 'archived';
  author: string;
  coAuthors: string[];
  createdAt: string;
  publishedAt?: string;
  summary: string;
  keyPoints: string[];
  citations: string[];
  tags: string[];
  wordCount: number;
  isPublic: boolean;
}

const JudgeOpinionsPage: React.FC = () => {
  const router = useRouter();
  const { session, status, isAuthorized } = useRequireRole([USER_ROLES.JUDGE, USER_ROLES.ADMIN, USER_ROLES.SUPERADMIN, USER_ROLES.SUPERADMIN]);
  const [selectedType, setSelectedType] = useState<string>('all');
  const [selectedStatus, setSelectedStatus] = useState<string>('all');
  const [searchTerm, setSearchTerm] = useState('');
  const [showPublic, setShowPublic] = useState(true);

  // Mock data for legal opinions
  const [opinions] = useState<LegalOpinion[]>([
    {
      id: '1',
      title: 'Interpretation of Section 8 Charter Rights in Digital Age',
      caseNumber: 'CR-2025-001',
      caseTitle: 'State v. Johnson',
      opinionType: 'majority',
      status: 'published',
      author: 'Hon. Marie Dubois',
      coAuthors: ['Hon. Jean-Pierre Tremblay'],
      createdAt: '2025-06-25 10:30',
      publishedAt: '2025-06-28 14:00',
      summary: 'This opinion addresses the application of Section 8 Charter rights to digital evidence obtained through warrantless searches of electronic devices. The majority holds that reasonable expectation of privacy extends to digital communications and requires judicial authorization for access.',
      keyPoints: [
        'Digital communications enjoy constitutional protection',
        'Warrantless searches of electronic devices violate Section 8',
        'Exigent circumstances exception narrowly construed',
        'Good faith exception applies to law enforcement reliance on prior precedent'
      ],
      citations: [
        'R. v. Spencer, 2014 SCC 43',
        'R. v. Marakah, 2017 SCC 59',
        'R. v. Reeves, 2018 SCC 56'
      ],
      tags: ['charter', 'digital_privacy', 'section_8', 'electronic_evidence'],
      wordCount: 4500,
      isPublic: true
    },
    {
      id: '2',
      title: 'Concurring Opinion on Contract Interpretation Principles',
      caseNumber: 'CV-2025-045',
      caseTitle: 'Smith v. Corporation Ltd',
      opinionType: 'concurring',
      status: 'published',
      author: 'Hon. Marie Dubois',
      coAuthors: [],
      createdAt: '2025-06-20 15:45',
      publishedAt: '2025-06-22 09:30',
      summary: 'While agreeing with the majority\'s conclusion, this concurring opinion emphasizes the importance of contextual interpretation in contract law and the need to consider commercial realities when determining parties\' intentions.',
      keyPoints: [
        'Contextual interpretation remains paramount',
        'Commercial context informs contractual intent',
        'Objective test applies to reasonable person standard',
        'Precedent should guide but not constrain interpretation'
      ],
      citations: [
        'Sattva Capital Corp. v. Creston Moly Corp., 2014 SCC 53',
        'Ledcor Construction Ltd. v. Northbridge Indemnity Insurance Co., 2016 SCC 37'
      ],
      tags: ['contract_law', 'interpretation', 'commercial_law', 'contextual_analysis'],
      wordCount: 2800,
      isPublic: true
    },
    {
      id: '3',
      title: 'Dissenting Opinion on Administrative Law Standards',
      caseNumber: 'AD-2025-078',
      caseTitle: 'Department of Transportation v. Chen',
      opinionType: 'dissenting',
      status: 'draft',
      author: 'Hon. Marie Dubois',
      coAuthors: [],
      createdAt: '2025-06-30 11:20',
      publishedAt: undefined,
      summary: 'This dissenting opinion argues that the majority\'s application of the reasonableness standard is overly deferential and fails to provide adequate protection for individual rights against administrative overreach.',
      keyPoints: [
        'Reasonableness standard requires meaningful review',
        'Administrative decisions must be justified and transparent',
        'Individual rights protection paramount',
        'Judicial oversight essential for accountability'
      ],
      citations: [
        'Canada (Minister of Citizenship and Immigration) v. Vavilov, 2019 SCC 65',
        'Dunsmuir v. New Brunswick, 2008 SCC 9'
      ],
      tags: ['administrative_law', 'reasonableness', 'judicial_review', 'individual_rights'],
      wordCount: 3200,
      isPublic: false
    },
    {
      id: '4',
      title: 'Per Curiam Opinion on Procedural Fairness',
      caseNumber: 'FA-2025-012',
      caseTitle: 'In re: Estate of Williams',
      opinionType: 'per_curiam',
      status: 'review',
      author: 'Court',
      coAuthors: ['Hon. Marie Dubois', 'Hon. Jean-Pierre Tremblay', 'Hon. Sarah Chen'],
      createdAt: '2025-06-29 16:15',
      publishedAt: undefined,
      summary: 'This per curiam opinion establishes clear guidelines for procedural fairness in estate administration proceedings, emphasizing the importance of notice, opportunity to be heard, and impartial decision-making.',
      keyPoints: [
        'Procedural fairness applies to estate administration',
        'Notice requirements must be strictly observed',
        'Opportunity to be heard fundamental to fairness',
        'Impartial decision-making essential'
      ],
      citations: [
        'Baker v. Canada (Minister of Citizenship and Immigration), [1999] 2 SCR 817',
        'Knight v. Indian Head School Division No. 19, [1990] 1 SCR 653'
      ],
      tags: ['procedural_fairness', 'estate_law', 'administrative_law', 'due_process'],
      wordCount: 1800,
      isPublic: false
    }
  ]);

  const getTypeColor = (type: string) => {
    switch (type) {
      case 'majority': return 'bg-green-100 text-green-800';
      case 'concurring': return 'bg-blue-100 text-blue-800';
      case 'dissenting': return 'bg-red-100 text-red-800';
      case 'per_curiam': return 'bg-purple-100 text-purple-800';
      default: return 'bg-gray-100 text-gray-800';
    }
  };

  const getStatusColor = (status: string) => {
    switch (status) {
      case 'draft': return 'bg-yellow-100 text-yellow-800';
      case 'review': return 'bg-orange-100 text-orange-800';
      case 'published': return 'bg-green-100 text-green-800';
      case 'archived': return 'bg-gray-100 text-gray-800';
      default: return 'bg-gray-100 text-gray-800';
    }
  };

  const filteredOpinions = opinions.filter(opinion => {
    const matchesType = selectedType === 'all' || opinion.opinionType === selectedType;
    const matchesStatus = selectedStatus === 'all' || opinion.status === selectedStatus;
    const matchesSearch = opinion.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
                         opinion.summary.toLowerCase().includes(searchTerm.toLowerCase()) ||
                         opinion.caseNumber.toLowerCase().includes(searchTerm.toLowerCase());
    const matchesVisibility = showPublic || !opinion.isPublic;
    return matchesType && matchesStatus && matchesSearch && matchesVisibility;
  });

  if (status === 'loading') {
    return (
      <div className="min-h-screen bg-gray-50 flex items-center justify-center">
        <div className="text-center">
          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
          <p className="mt-4 text-gray-600">Loading legal opinions...</p>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-gray-50">
      {/* Header */}
      <div className="bg-white shadow-sm border-b">
        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
          <div className="flex justify-between items-center py-6">
            <div>
              <h1 className="text-3xl font-bold text-gray-900">Legal Opinions</h1>
              <p className="mt-1 text-sm text-gray-500">
                Manage and publish legal opinions, rulings, and judicial decisions
              </p>
            </div>
            <div className="flex space-x-3">
              <button
                onClick={() => router.push('/judge/dashboard')}
                className="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
              >
                ← Back to Dashboard
              </button>
              <button className="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                + New Opinion
              </button>
            </div>
          </div>
        </div>
      </div>

      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
        {/* Stats Overview */}
        <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
          <div className="bg-white overflow-hidden shadow rounded-lg">
            <div className="p-5">
              <div className="flex items-center">
                <div className="flex-shrink-0">
                  <div className="w-8 h-8 bg-blue-500 rounded-md flex items-center justify-center">
                    <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
                    </svg>
                  </div>
                </div>
                <div className="ml-5 w-0 flex-1">
                  <dl>
                    <dt className="text-sm font-medium text-gray-500 truncate">Total Opinions</dt>
                    <dd className="text-lg font-medium text-gray-900">{opinions.length}</dd>
                  </dl>
                </div>
              </div>
            </div>
          </div>

          <div className="bg-white overflow-hidden shadow rounded-lg">
            <div className="p-5">
              <div className="flex items-center">
                <div className="flex-shrink-0">
                  <div className="w-8 h-8 bg-green-500 rounded-md flex items-center justify-center">
                    <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
                    </svg>
                  </div>
                </div>
                <div className="ml-5 w-0 flex-1">
                  <dl>
                    <dt className="text-sm font-medium text-gray-500 truncate">Published</dt>
                    <dd className="text-lg font-medium text-gray-900">
                      {opinions.filter(o => o.status === 'published').length}
                    </dd>
                  </dl>
                </div>
              </div>
            </div>
          </div>

          <div className="bg-white overflow-hidden shadow rounded-lg">
            <div className="p-5">
              <div className="flex items-center">
                <div className="flex-shrink-0">
                  <div className="w-8 h-8 bg-yellow-500 rounded-md flex items-center justify-center">
                    <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
                    </svg>
                  </div>
                </div>
                <div className="ml-5 w-0 flex-1">
                  <dl>
                    <dt className="text-sm font-medium text-gray-500 truncate">In Progress</dt>
                    <dd className="text-lg font-medium text-gray-900">
                      {opinions.filter(o => o.status === 'draft' || o.status === 'review').length}
                    </dd>
                  </dl>
                </div>
              </div>
            </div>
          </div>

          <div className="bg-white overflow-hidden shadow rounded-lg">
            <div className="p-5">
              <div className="flex items-center">
                <div className="flex-shrink-0">
                  <div className="w-8 h-8 bg-purple-500 rounded-md flex items-center justify-center">
                    <svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
                    </svg>
                  </div>
                </div>
                <div className="ml-5 w-0 flex-1">
                  <dl>
                    <dt className="text-sm font-medium text-gray-500 truncate">Total Words</dt>
                    <dd className="text-lg font-medium text-gray-900">
                      {opinions.reduce((sum, opinion) => sum + opinion.wordCount, 0).toLocaleString()}
                    </dd>
                  </dl>
                </div>
              </div>
            </div>
          </div>
        </div>

        {/* Filters */}
        <div className="bg-white shadow rounded-lg mb-6">
          <div className="px-6 py-4 border-b border-gray-200">
            <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between space-y-4 sm:space-y-0">
              <div className="flex-1 max-w-lg">
                <label htmlFor="search" className="sr-only">Search opinions</label>
                <div className="relative">
                  <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
                    <svg className="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
                    </svg>
                  </div>
                  <input
                    id="search"
                    name="search"
                    className="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                    placeholder="Search opinions by title, summary, or case number..."
                    type="search"
                    value={searchTerm}
                    onChange={(e) => setSearchTerm(e.target.value)}
                  />
                </div>
              </div>
              <div className="flex space-x-4">
                <select
                  value={selectedType}
                  onChange={(e) => setSelectedType(e.target.value)}
                  className="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
                >
                  <option value="all">All Types</option>
                  <option value="majority">Majority</option>
                  <option value="concurring">Concurring</option>
                  <option value="dissenting">Dissenting</option>
                  <option value="per_curiam">Per Curiam</option>
                </select>
                <select
                  value={selectedStatus}
                  onChange={(e) => setSelectedStatus(e.target.value)}
                  className="block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
                >
                  <option value="all">All Statuses</option>
                  <option value="draft">Draft</option>
                  <option value="review">Review</option>
                  <option value="published">Published</option>
                  <option value="archived">Archived</option>
                </select>
                <label className="flex items-center">
                  <input
                    type="checkbox"
                    checked={showPublic}
                    onChange={(e) => setShowPublic(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">Show Public</span>
                </label>
              </div>
            </div>
          </div>
        </div>

        {/* Opinions List */}
        <div className="space-y-6">
          {filteredOpinions.map((opinion) => (
            <div key={opinion.id} className="bg-white shadow rounded-lg overflow-hidden">
              <div className="px-6 py-4 border-b border-gray-200">
                <div className="flex items-center justify-between">
                  <div className="flex items-center space-x-3">
                    <h3 className="text-lg font-medium text-gray-900">{opinion.title}</h3>
                    {!opinion.isPublic && (
                      <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">
                        Private
                      </span>
                    )}
                  </div>
                  <div className="flex items-center space-x-2">
                    <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getTypeColor(opinion.opinionType)}`}>
                      {opinion.opinionType.replace('_', ' ')}
                    </span>
                    <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getStatusColor(opinion.status)}`}>
                      {opinion.status}
                    </span>
                  </div>
                </div>
                <div className="mt-2">
                  <p className="text-sm text-gray-600">
                    Case: <span className="font-medium">{opinion.caseNumber}</span> - {opinion.caseTitle}
                  </p>
                  <p className="text-xs text-gray-500 mt-1">
                    Author: {opinion.author}
                    {opinion.coAuthors.length > 0 && ` (with ${opinion.coAuthors.join(', ')})`}
                  </p>
                  <p className="text-xs text-gray-500">
                    Created: {new Date(opinion.createdAt).toLocaleDateString()}
                    {opinion.publishedAt && ` • Published: ${new Date(opinion.publishedAt).toLocaleDateString()}`}
                  </p>
                </div>
              </div>
              <div className="px-6 py-4">
                <p className="text-sm text-gray-700 mb-4">{opinion.summary}</p>
                
                <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                  <div>
                    <h4 className="text-sm font-medium text-gray-900 mb-2">Key Points</h4>
                    <ul className="text-sm text-gray-600 space-y-1">
                      {opinion.keyPoints.map((point, index) => (
                        <li key={index} className="flex items-start">
                          <span className="text-blue-500 mr-2">•</span>
                          {point}
                        </li>
                      ))}
                    </ul>
                  </div>
                  <div>
                    <h4 className="text-sm font-medium text-gray-900 mb-2">Citations</h4>
                    <ul className="text-sm text-gray-600 space-y-1">
                      {opinion.citations.map((citation, index) => (
                        <li key={index} className="flex items-start">
                          <span className="text-green-500 mr-2">•</span>
                          {citation}
                        </li>
                      ))}
                    </ul>
                  </div>
                </div>

                {opinion.tags.length > 0 && (
                  <div className="mt-4 flex flex-wrap gap-2">
                    {opinion.tags.map((tag, index) => (
                      <span
                        key={index}
                        className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
                      >
                        #{tag}
                      </span>
                    ))}
                  </div>
                )}
              </div>
              <div className="px-6 py-3 bg-gray-50 border-t border-gray-200">
                <div className="flex justify-between items-center">
                  <p className="text-xs text-gray-500">
                    Word count: {opinion.wordCount.toLocaleString()}
                  </p>
                  <div className="flex space-x-2">
                    <button className="inline-flex items-center px-3 py-1 border border-transparent text-sm leading-4 font-medium rounded-md text-blue-700 bg-blue-100 hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                      Edit
                    </button>
                    <button className="inline-flex items-center px-3 py-1 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                      View Full
                    </button>
                    <button className="inline-flex items-center px-3 py-1 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                      {opinion.status === 'published' ? 'Unpublish' : 'Publish'}
                    </button>
                  </div>
                </div>
              </div>
            </div>
          ))}
        </div>

        {filteredOpinions.length === 0 && (
          <div className="text-center py-12">
            <svg className="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
            </svg>
            <h3 className="mt-2 text-sm font-medium text-gray-900">No opinions found</h3>
            <p className="mt-1 text-sm text-gray-500">
              Try adjusting your search or filter criteria, or create a new opinion.
            </p>
            <div className="mt-6">
              <button className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
                + Create Opinion
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

export default JudgeOpinionsPage; 

CasperSecurity Mini