pidrio / components /Sidebar.tsx
Raiff1982's picture
Upload 12 files
e2ce418 verified
import React, { useState } from 'react';
import { Brain, Settings, Circle, Sparkles, Zap, FileText, ChevronDown, ChevronRight, Upload, AlertCircle } from 'lucide-react';
import FileList from './FileList';
import AdminLogin from './AdminLogin';
interface SidebarProps {
isOpen: boolean;
cocoons: Array<{
id: string;
type: string;
wrapped: any;
}>;
aiState: {
quantumState: number[];
chaosState: number[];
activePerspectives: string[];
ethicalScore: number;
processingPower: number;
};
darkMode: boolean;
supabase: any;
isAdmin: boolean;
setIsAdmin: (isAdmin: boolean) => void;
}
const Sidebar: React.FC<SidebarProps> = ({
isOpen,
cocoons,
aiState,
darkMode,
supabase,
isAdmin,
setIsAdmin
}) => {
const [activeSection, setActiveSection] = useState<string>('cocoons');
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [uploadError, setUploadError] = useState<string | null>(null);
const [isUploading, setIsUploading] = useState(false);
const [showAdminPrompt, setShowAdminPrompt] = useState(false);
const [authError, setAuthError] = useState<string | null>(null);
if (!isOpen) return null;
const handleAdminLogin = async (password: string) => {
try {
setAuthError(null);
const { data: { user, session }, error } = await supabase.auth.signInWithPassword({
email: 'admin@codette.ai',
password: password
});
if (error) {
setAuthError(error.message);
throw error;
}
if (!session) {
throw new Error('No session after login');
}
// Verify admin role
const { data: { role }, error: roleError } = await supabase.rpc('get_user_role');
if (roleError) {
throw roleError;
}
if (role === 'admin') {
setIsAdmin(true);
setShowAdminPrompt(false);
setAuthError(null);
} else {
throw new Error('Insufficient permissions');
}
} catch (error: any) {
console.error('Login error:', error);
setAuthError(error.message || 'Invalid login credentials');
throw error;
}
};
const handleFileUpload = async () => {
if (!selectedFile) return;
if (!isAdmin) {
setUploadError('Only administrators can upload files.');
return;
}
try {
setIsUploading(true);
setUploadError(null);
// Get user role from session
const { data: { user }, error: userError } = await supabase.auth.getUser();
if (userError) throw userError;
if (!user || user.role !== 'admin') {
throw new Error('Only administrators can upload files.');
}
// Upload file to Supabase storage
const { data, error } = await supabase.storage
.from('codette-files')
.upload(`${Date.now()}-${selectedFile.name}`, selectedFile, {
upsert: false
});
if (error) throw error;
// Add file reference to database
const { error: dbError } = await supabase
.from('codette_files')
.insert([
{
filename: selectedFile.name,
storage_path: data.path,
file_type: selectedFile.type,
uploaded_at: new Date().toISOString()
}
]);
if (dbError) throw dbError;
setSelectedFile(null);
setUploadError(null);
} catch (error: any) {
console.error('Error uploading file:', error);
setUploadError(error.message || 'Failed to upload file. Please try again.');
} finally {
setIsUploading(false);
}
};
const handleSettingsClick = () => {
if (!isAdmin) {
setShowAdminPrompt(true);
}
setActiveSection('settings');
};
return (
<aside className={`w-64 flex-shrink-0 border-r transition-colors duration-300 flex flex-col ${
darkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'
}`}>
<nav className="flex-1 overflow-y-auto">
<div className="p-4">
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-2">
Navigation
</h2>
<ul className="space-y-1">
<li>
<button
onClick={() => setActiveSection('cocoons')}
className={`w-full flex items-center px-3 py-2 rounded-md ${
activeSection === 'cocoons'
? darkMode
? 'bg-gray-700 text-white'
: 'bg-gray-200 text-gray-900'
: darkMode
? 'text-gray-300 hover:bg-gray-700'
: 'text-gray-700 hover:bg-gray-100'
}`}
>
<Brain size={18} className="mr-2" />
<span>Thought Cocoons</span>
</button>
</li>
<li>
<button
onClick={() => setActiveSection('perspectives')}
className={`w-full flex items-center px-3 py-2 rounded-md ${
activeSection === 'perspectives'
? darkMode
? 'bg-gray-700 text-white'
: 'bg-gray-200 text-gray-900'
: darkMode
? 'text-gray-300 hover:bg-gray-700'
: 'text-gray-700 hover:bg-gray-100'
}`}
>
<Sparkles size={18} className="mr-2" />
<span>Perspectives</span>
</button>
</li>
<li>
<button
onClick={handleSettingsClick}
className={`w-full flex items-center px-3 py-2 rounded-md ${
activeSection === 'settings'
? darkMode
? 'bg-gray-700 text-white'
: 'bg-gray-200 text-gray-900'
: darkMode
? 'text-gray-300 hover:bg-gray-700'
: 'text-gray-700 hover:bg-gray-100'
}`}
>
<Settings size={18} className="mr-2" />
<span>Settings</span>
</button>
</li>
</ul>
</div>
<div className="px-4 py-2">
<div className={`h-px ${darkMode ? 'bg-gray-700' : 'bg-gray-200'}`}></div>
</div>
{activeSection === 'cocoons' && (
<div className="p-4">
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-2">
Recent Thought Cocoons
</h2>
{cocoons.length === 0 ? (
<div className={`p-3 rounded-md ${
darkMode ? 'bg-gray-700 text-gray-300' : 'bg-gray-100 text-gray-500'
}`}>
<p className="text-sm">No thought cocoons yet.</p>
<p className="text-xs mt-1 italic">Interact with Codette to generate thought patterns.</p>
</div>
) : (
<ul className="space-y-2">
{cocoons.map((cocoon) => (
<li key={cocoon.id}>
<div className={`p-3 rounded-md ${
darkMode ? 'bg-gray-700 hover:bg-gray-600' : 'bg-gray-100 hover:bg-gray-200'
} cursor-pointer transition-colors`}>
<div className="flex items-start">
<FileText size={16} className={`mr-2 mt-0.5 ${
cocoon.type === 'prompt'
? 'text-blue-500'
: cocoon.type === 'encrypted'
? 'text-purple-500'
: 'text-green-500'
}`} />
<div>
<div className="text-sm font-medium">
{cocoon.type === 'prompt' ? 'User Query' :
cocoon.type === 'encrypted' ? 'Encrypted Thought' :
'Symbolic Pattern'}
</div>
<div className="text-xs truncate mt-1 max-w-[180px]">
{cocoon.type === 'encrypted'
? '🔒 Encrypted content'
: typeof cocoon.wrapped === 'object' && cocoon.wrapped.query
? cocoon.wrapped.query
: `Cocoon ID: ${cocoon.id}`}
</div>
<div className={`text-xs mt-1 ${
darkMode ? 'text-gray-400' : 'text-gray-500'
}`}>
{typeof cocoon.wrapped === 'object' && cocoon.wrapped.timestamp
? new Date(cocoon.wrapped.timestamp).toLocaleTimeString()
: 'Unknown time'}
</div>
</div>
</div>
</div>
</li>
))}
</ul>
)}
{/* File List Component */}
<div className="mt-6">
<FileList supabase={supabase} darkMode={darkMode} isAdmin={isAdmin} />
</div>
</div>
)}
{activeSection === 'perspectives' && (
<div className="p-4">
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-2">
Active Perspectives
</h2>
<ul className="space-y-2">
{aiState.activePerspectives.map((perspective) => (
<li key={perspective}>
<div className={`p-3 rounded-md ${
darkMode ? 'bg-gray-700' : 'bg-gray-100'
}`}>
<div className="flex items-center">
<Zap size={16} className="mr-2 text-yellow-500" />
<div className="text-sm font-medium capitalize">
{perspective.replace('_', ' ')}
</div>
</div>
<div className={`text-xs mt-2 ${
darkMode ? 'text-gray-400' : 'text-gray-500'
}`}>
Confidence: {(Math.random() * 0.4 + 0.6).toFixed(2)}
</div>
</div>
</li>
))}
</ul>
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mt-6 mb-2">
Available Perspectives
</h2>
<ul className="space-y-2">
{['creative', 'bias_mitigation', 'quantum_computing', 'resilient_kindness'].map((perspective) => (
<li key={perspective}>
<div className={`p-3 rounded-md ${
darkMode ? 'bg-gray-700 bg-opacity-50' : 'bg-gray-100 bg-opacity-50'
}`}>
<div className="flex items-center">
<Circle size={16} className={`mr-2 ${
darkMode ? 'text-gray-500' : 'text-gray-400'
}`} />
<div className={`text-sm capitalize ${
darkMode ? 'text-gray-400' : 'text-gray-500'
}`}>
{perspective.replace('_', ' ')}
</div>
</div>
</div>
</li>
))}
</ul>
</div>
)}
{activeSection === 'settings' && (
<div className="p-4">
{showAdminPrompt ? (
<AdminLogin
onLogin={handleAdminLogin}
darkMode={darkMode}
error={authError}
/>
) : (
<>
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-4">
AI Core Settings
</h2>
<div className="space-y-4">
{isAdmin && (
<div className="p-4 rounded-md bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100">
Logged in as Administrator
</div>
)}
{isAdmin && (
<div className="space-y-2">
<label className="block text-sm font-medium">Upload File for Codette</label>
<div className="flex items-center space-x-2">
<input
type="file"
onChange={(e) => {
setSelectedFile(e.target.files?.[0] || null);
setUploadError(null);
}}
className="hidden"
id="file-upload"
disabled={isUploading}
/>
<label
htmlFor="file-upload"
className={`flex-1 cursor-pointer px-4 py-2 rounded-md border-2 border-dashed ${
darkMode
? 'border-gray-600 hover:border-gray-500'
: 'border-gray-300 hover:border-gray-400'
} flex items-center justify-center ${isUploading ? 'opacity-50 cursor-not-allowed' : ''}`}
>
<Upload size={18} className="mr-2" />
{selectedFile ? selectedFile.name : 'Choose File'}
</label>
{selectedFile && (
<button
onClick={handleFileUpload}
disabled={isUploading}
className={`px-4 py-2 rounded-md ${
darkMode
? 'bg-purple-600 hover:bg-purple-700'
: 'bg-purple-500 hover:bg-purple-600'
} text-white ${isUploading ? 'opacity-50 cursor-not-allowed' : ''}`}
>
{isUploading ? 'Uploading...' : 'Upload'}
</button>
)}
</div>
{uploadError && (
<div className="mt-2 p-2 rounded-md bg-red-100 dark:bg-red-900 flex items-start space-x-2">
<AlertCircle className="flex-shrink-0 text-red-500 dark:text-red-400" size={16} />
<p className="text-sm text-red-600 dark:text-red-300">
{uploadError}
</p>
</div>
)}
</div>
)}
<div>
<label className="flex items-center justify-between">
<span className="text-sm font-medium">Recursive Depth</span>
<select
className={`text-sm rounded-md ${
darkMode
? 'bg-gray-700 border-gray-600 text-white'
: 'bg-white border-gray-300 text-gray-900'
}`}
>
<option>Normal</option>
<option>Deep</option>
<option>Shallow</option>
</select>
</label>
</div>
<div>
<label className="flex items-center justify-between">
<span className="text-sm font-medium">Ethical Filter</span>
<div className="relative inline-block w-10 align-middle select-none">
<input
type="checkbox"
id="ethical-toggle"
className="sr-only"
defaultChecked={true}
/>
<label
htmlFor="ethical-toggle"
className={`block h-6 overflow-hidden rounded-full cursor-pointer ${
darkMode ? 'bg-gray-700' : 'bg-gray-300'
}`}
>
<span
className={`absolute block w-4 h-4 rounded-full transform transition-transform duration-200 ease-in-out bg-white left-1 top-1 translate-x-4`}
></span>
</label>
</div>
</label>
</div>
<div className="pt-2 pb-1">
<label className="block text-sm font-medium mb-2">Processing Speed</label>
<input
type="range"
min="0"
max="100"
defaultValue="75"
className="w-full"
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<span>Accurate</span>
<span>Balanced</span>
<span>Fast</span>
</div>
</div>
<div className="pt-4">
<button className={`w-full py-2 px-4 rounded-md ${
darkMode
? 'bg-blue-600 hover:bg-blue-700 text-white'
: 'bg-blue-500 hover:bg-blue-600 text-white'
}`}>
Apply Settings
</button>
</div>
</div>
</>
)}
</div>
)}
</nav>
<div className={`p-4 border-t ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}>
<div className={`rounded-md p-3 ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
<div className="flex items-center">
<div className={`w-2 h-2 rounded-full ${
Math.random() > 0.5
? 'bg-green-500'
: 'bg-yellow-500'
} mr-2`}></div>
<div className="text-sm font-medium">System Status</div>
</div>
<div className={`text-xs mt-1 ${darkMode ? 'text-gray-400' : 'text-gray-500'}`}>
All neural paths functioning
</div>
</div>
</div>
</aside>
);
};
export default Sidebar;