AI / src /components /AccountManagerModal.tsx
Aura-Studio's picture
Update src/components/AccountManagerModal.tsx
1f6d247 verified
Raw History Blame Contribute Delete
155 kB
import React, { useState, useEffect } from "react";
import {
HardDrive,
Folder,
Image as ImageIcon,
MessageSquare,
Sparkles,
Pin,
Trash2,
Download,
RefreshCw,
AlertTriangle,
CheckCircle2,
X,
User,
LogOut,
FileText,
ExternalLink,
ShieldCheck,
Cloud,
Database,
UploadCloud,
Key,
Code2,
Zap,
Search,
CheckSquare,
Square,
Trash,
AlertOctagon,
Clock,
Info,
CreditCard,
Check,
Crown,
ChevronRight,
Send,
Users,
Sliders,
Award,
Layers,
Copy,
Smartphone,
Settings,
Volume2,
Sun,
Palette,
FolderTree,
FileCode,
Compass,
MapPin,
Globe,
Terminal,
Activity,
CheckCircle,
ToggleLeft,
ToggleRight,
Sparkle,
Cpu,
BookOpen
} from "lucide-react";
import { TIER_CONFIGS, SubscriptionTier } from "../types/subscription";
import { AURA_THEMES, AuraTheme } from "../types/theme";
export interface DataaiFileItem {
name: string;
relativePath: string;
hfStoragePath: string;
sizeBytes: number;
sizeFormatted: string;
updatedAt: string;
category: "users" | "chats" | "media" | "config" | "system";
isDir: boolean;
}
function formatBytes(bytes: number): string {
if (!bytes || bytes < 0) return "0 B";
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
interface StoredItem {
id: string;
name: string;
folder: "media storage" | "chats" | "generated media" | "pinned chats";
sizeBytes: number;
sizeFormatted: string;
updatedAt: string;
relativePath: string;
dataUrl?: string;
metadata?: any;
}
interface UpgradeRequest {
id: string;
username: string;
requestedTier: string;
amount: number;
paymentMethod: string;
transactionId: string;
phoneNumber?: string;
note?: string;
status: "pending" | "approved" | "declined";
createdAt: string;
processedAt?: string;
processedBy?: string;
}
interface UserSummary {
id: string;
username: string;
tier: string;
isAdmin: boolean;
createdAt: string;
lastLoginAt?: string;
usedFormatted: string;
maxFormatted: string;
daysRemaining?: number;
tierExpiresAtFormatted?: string;
hasCustomGptAccess?: boolean;
}
interface AccountManagerModalProps {
isOpen: boolean;
onClose: () => void;
currentUser: {
id: string;
username: string;
createdAt?: string;
tier?: string;
token?: string;
stats?: any;
} | null;
onSignOut: () => void;
onRefreshUserStats: () => Promise<void>;
onSyncChats: () => Promise<void>;
onOpenAuraCodex?: () => void;
// Settings & Voice Props
selectedVoiceURI?: string;
onSelectVoice?: (voiceURI: string) => void;
auraThemeId?: string;
onSelectAuraTheme?: (themeId: string) => void;
autoThemeEnabled?: boolean;
onToggleAutoTheme?: (enabled: boolean) => void;
darkMode?: boolean;
onToggleDarkMode?: (dark: boolean) => void;
themeManualOverrideUntil?: number;
initialTab?: "storage" | "dataai_explorer" | "subscriptions" | "my_requests" | "admin_console" | "settings";
// Accuracy & Google AI Features Props
accuracyMode?: "perfect" | "balanced" | "fast";
onSelectAccuracyMode?: (mode: "perfect" | "balanced" | "fast") => void;
isOxfordContextActive?: boolean;
onToggleOxfordContext?: (active: boolean) => void;
onOpenOxfordModal?: () => void;
googleSearchGrounding?: boolean;
onToggleGoogleSearchGrounding?: (enabled: boolean) => void;
googleMapsGrounding?: boolean;
onToggleGoogleMapsGrounding?: (enabled: boolean) => void;
fastFlashAccelerator?: boolean;
onToggleFastFlashAccelerator?: (enabled: boolean) => void;
googleCodeExecution?: boolean;
onToggleGoogleCodeExecution?: (enabled: boolean) => void;
firebaseEnabled?: boolean;
onToggleFirebaseEnabled?: (enabled: boolean) => void;
understandingMode?: string;
onSelectUnderstandingMode?: (mode: string) => void;
customApiKey?: string;
onUpdateCustomApiKey?: (key: string) => void;
}
export default function AccountManagerModal({
isOpen,
onClose,
currentUser,
onSignOut,
onRefreshUserStats,
onSyncChats,
onOpenAuraCodex,
selectedVoiceURI = "",
onSelectVoice,
auraThemeId = "aura-nebula",
onSelectAuraTheme,
autoThemeEnabled = true,
onToggleAutoTheme,
darkMode = true,
onToggleDarkMode,
themeManualOverrideUntil = 0,
initialTab = "subscriptions",
accuracyMode = "perfect",
onSelectAccuracyMode,
isOxfordContextActive = false,
onToggleOxfordContext,
onOpenOxfordModal,
googleSearchGrounding = false,
onToggleGoogleSearchGrounding,
googleMapsGrounding = false,
onToggleGoogleMapsGrounding,
fastFlashAccelerator = true,
onToggleFastFlashAccelerator,
googleCodeExecution = false,
onToggleGoogleCodeExecution,
firebaseEnabled = false,
onToggleFirebaseEnabled,
understandingMode = "ultra-precision",
onSelectUnderstandingMode,
customApiKey = "",
onUpdateCustomApiKey
}: AccountManagerModalProps) {
// Navigation Tabs: storage | dataai_explorer | subscriptions | my_requests | admin_console | settings
const isHarshaAdmin = currentUser?.username?.toLowerCase() === "harsha" || currentUser?.stats?.isAdmin;
const [activeTab, setActiveTab] = useState<"storage" | "dataai_explorer" | "subscriptions" | "my_requests" | "admin_console" | "settings">(initialTab);
const getAuthHeaders = () => {
return currentUser?.token ? { "X-Aura-Auth": currentUser.token } : {};
};
useEffect(() => {
if (initialTab) {
setActiveTab(initialTab);
}
}, [initialTab]);
// Speech Synthesis Voices State
const [voices, setVoices] = useState<SpeechSynthesisVoice[]>([]);
useEffect(() => {
if (!("speechSynthesis" in window)) return;
const updateVoices = () => {
const available = window.speechSynthesis.getVoices();
setVoices(available || []);
};
updateVoices();
window.speechSynthesis.onvoiceschanged = updateVoices;
return () => {
if ("speechSynthesis" in window) {
window.speechSynthesis.onvoiceschanged = null;
}
};
}, []);
const handleTestVoice = (voiceURI?: string) => {
if (!("speechSynthesis" in window)) {
alert("Speech Synthesis is not supported in this browser.");
return;
}
window.speechSynthesis.cancel();
const testUtterance = new SpeechSynthesisUtterance("Hello! I am Aura AI, speaking with your chosen voice.");
if (voiceURI && voices.length > 0) {
const selected = voices.find(v => v.voiceURI === voiceURI || v.name === voiceURI);
if (selected) testUtterance.voice = selected;
}
testUtterance.rate = 1;
testUtterance.pitch = 1;
window.speechSynthesis.speak(testUtterance);
};
// Password Change State
const [currentPasswordInput, setCurrentPasswordInput] = useState<string>("");
const [newPasswordInput, setNewPasswordInput] = useState<string>("");
const [confirmPasswordInput, setConfirmPasswordInput] = useState<string>("");
const [passwordStatusMsg, setPasswordStatusMsg] = useState<{ success: boolean; text: string } | null>(null);
const [isSubmittingPassword, setIsSubmittingPassword] = useState<boolean>(false);
const handleChangePasswordSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setPasswordStatusMsg(null);
if (!currentPasswordInput || !newPasswordInput) {
setPasswordStatusMsg({ success: false, text: "Please enter both current and new password." });
return;
}
if (newPasswordInput !== confirmPasswordInput) {
setPasswordStatusMsg({ success: false, text: "New password and confirmation do not match." });
return;
}
if (newPasswordInput.length < 6) {
setPasswordStatusMsg({ success: false, text: "New password must be at least 6 characters long." });
return;
}
setIsSubmittingPassword(true);
try {
const res = await fetch("/api/auth/change-password", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: currentUser?.username,
currentPassword: currentPasswordInput,
newPassword: newPasswordInput
})
});
const data = await res.json();
if (data.success) {
setPasswordStatusMsg({ success: true, text: data.message || "Password updated successfully!" });
setCurrentPasswordInput("");
setNewPasswordInput("");
setConfirmPasswordInput("");
} else {
setPasswordStatusMsg({ success: false, text: data.error || "Failed to update password." });
}
} catch (err: any) {
setPasswordStatusMsg({ success: false, text: err.message || "Network error updating password." });
} finally {
setIsSubmittingPassword(false);
}
};
// Storage Tab States
const [activeFolder, setActiveFolder] = useState<"all" | "media storage" | "chats" | "generated media" | "pinned chats">("all");
const [items, setItems] = useState<StoredItem[]>([]);
const [stats, setStats] = useState<any>(currentUser?.stats || null);
const [loading, setLoading] = useState<boolean>(false);
const [deletingId, setDeletingId] = useState<string | null>(null);
const [bulkDeleting, setBulkDeleting] = useState<boolean>(false);
const [syncing, setSyncing] = useState<boolean>(false);
const [actionMessage, setActionMessage] = useState<string | null>(null);
const [previewItem, setPreviewItem] = useState<StoredItem | null>(null);
// Search & Bulk Selection State
const [searchQuery, setSearchQuery] = useState<string>("");
const [selectedItemIds, setSelectedItemIds] = useState<Set<string>>(new Set());
// Custom Modal Confirmation Dialogs
const [confirmDeleteModal, setConfirmDeleteModal] = useState<{ itemsToDelete: StoredItem[] } | null>(null);
const [confirmHfModal, setConfirmHfModal] = useState<{ action: "pull" | "push" } | null>(null);
// Hugging Face Persistence State
const [hfStatus, setHfStatus] = useState<any>(null);
const [hfSyncing, setHfSyncing] = useState<boolean>(false);
const [showHfTokenModal, setShowHfTokenModal] = useState<boolean>(false);
const [customHfToken, setCustomHfToken] = useState<string>("");
const [customHfRepo, setCustomHfRepo] = useState<string>("");
// DataAI Explorer & Hugging Face Bucket State
const [dataaiFiles, setDataaiFiles] = useState<DataaiFileItem[]>([]);
const [loadingDataai, setLoadingDataai] = useState<boolean>(false);
const [selectedDataaiFile, setSelectedDataaiFile] = useState<DataaiFileItem | null>(null);
const [selectedFileContent, setSelectedFileContent] = useState<string | null>(null);
const [selectedFileContentType, setSelectedFileContentType] = useState<string>("text/plain");
const [loadingFileContent, setLoadingFileContent] = useState<boolean>(false);
const [dataaiSearch, setDataaiSearch] = useState<string>("");
const [dataaiFilter, setDataaiFilter] = useState<"all" | "users" | "chats" | "media" | "config">("all");
const [hfTestLoading, setHfTestLoading] = useState<boolean>(false);
const [hfTestResult, setHfTestResult] = useState<any>(null);
// Subscriptions & Usage Limits State
const [usageData, setUsageData] = useState<any>(null);
const [loadingUsage, setLoadingUsage] = useState<boolean>(false);
const [upgradeModalTier, setUpgradeModalTier] = useState<SubscriptionTier | null>(null);
const [paymentMethodTab, setPaymentMethodTab] = useState<"gpay" | "razorpay">("gpay");
const [transactionIdInput, setTransactionIdInput] = useState<string>("");
const [copiedGpay, setCopiedGpay] = useState<boolean>(false);
const [copiedUpiId, setCopiedUpiId] = useState<boolean>(false);
const [isSubmittingTxn, setIsSubmittingTxn] = useState<boolean>(false);
const [paymentSuccessMsg, setPaymentSuccessMsg] = useState<string | null>(null);
// My Requests State
const [myRequests, setMyRequests] = useState<UpgradeRequest[]>([]);
const [loadingMyRequests, setLoadingMyRequests] = useState<boolean>(false);
// Admin Console State (Harsha)
const [adminRequests, setAdminRequests] = useState<UpgradeRequest[]>([]);
const [adminUsers, setAdminUsers] = useState<UserSummary[]>([]);
const [loadingAdminData, setLoadingAdminData] = useState<boolean>(false);
const [adminUserSearch, setAdminUserSearch] = useState<string>("");
const [changingTierUser, setChangingTierUser] = useState<{ username: string; tier: string } | null>(null);
// Fetch Storage Items
const fetchItems = async () => {
if (!currentUser) return;
setLoading(true);
try {
const res = await fetch(`/api/aura-store/items?username=${encodeURIComponent(currentUser.username)}&token=${encodeURIComponent(currentUser.token || "")}`);
const data = await res.json();
if (data.success) {
setItems(data.items || []);
if (data.stats) {
setStats(data.stats);
}
}
} catch (e) {
console.error("Failed to load Aura Store items:", e);
} finally {
setLoading(false);
}
};
// Fetch Usage & Limits
const fetchUsage = async () => {
const uname = currentUser?.username || localStorage.getItem("aura_user_name") || "";
setLoadingUsage(true);
try {
const res = await fetch(`/api/subscriptions/usage?username=${encodeURIComponent(uname)}`);
const data = await res.json();
if (data.success) {
setUsageData(data);
}
} catch (e) {
console.error("Failed to load usage data:", e);
} finally {
setLoadingUsage(false);
}
};
// Fetch User's Own Upgrade Requests
const fetchMyRequests = async () => {
if (!currentUser) return;
setLoadingMyRequests(true);
try {
const res = await fetch(`/api/subscriptions/my-requests?username=${encodeURIComponent(currentUser.username)}`);
const data = await res.json();
if (data.success) {
setMyRequests(data.requests || []);
}
} catch (e) {
console.error("Failed to load my requests:", e);
} finally {
setLoadingMyRequests(false);
}
};
// Fetch Admin Requests & Users
const fetchAdminData = async () => {
if (!currentUser || !isHarshaAdmin) return;
setLoadingAdminData(true);
try {
const [reqsRes, usersRes] = await Promise.all([
fetch(`/api/subscriptions/admin-requests?adminUsername=${encodeURIComponent(currentUser.username)}`),
fetch(`/api/subscriptions/admin-users?adminUsername=${encodeURIComponent(currentUser.username)}`)
]);
const reqsData = await reqsRes.json();
const usersData = await usersRes.json();
if (reqsData.success) setAdminRequests(reqsData.requests || []);
if (usersData.success) setAdminUsers(usersData.users || []);
} catch (e) {
console.error("Failed to load admin data:", e);
} finally {
setLoadingAdminData(false);
}
};
const fetchHfStatus = async () => {
try {
const res = await fetch("/api/aura-store/hf-sync-status");
const data = await res.json();
if (data.success) {
setHfStatus(data);
if (data.repo) setCustomHfRepo(data.repo);
}
} catch (e) {}
};
const fetchDataaiExplorer = async () => {
setLoadingDataai(true);
try {
const res = await fetch(`/api/aura-store/dataai-explorer?username=${encodeURIComponent(currentUser.username)}&token=${encodeURIComponent(currentUser.token || "")}`);
const data = await res.json();
if (data.success) {
setDataaiFiles(data.files || []);
if (data.files && data.files.length > 0) {
// If none selected or current selection is not in list, select first
setSelectedDataaiFile(prev => {
const exists = prev ? data.files.some((f: any) => f.relativePath === prev.relativePath) : false;
if (exists) return prev;
handleSelectDataaiFile(data.files[0]);
return data.files[0];
});
} else {
setSelectedDataaiFile(null);
setSelectedFileContent("");
}
}
} catch (e) {
console.error("Failed to load cloud files:", e);
} finally {
setLoadingDataai(false);
}
};
const handleSelectDataaiFile = async (file: DataaiFileItem) => {
setSelectedDataaiFile(file);
setLoadingFileContent(true);
try {
const res = await fetch(`/api/aura-store/dataai-file?path=${encodeURIComponent(file.relativePath)}&username=${encodeURIComponent(currentUser.username)}&token=${encodeURIComponent(currentUser.token || "")}`);
const data = await res.json();
if (data.success) {
setSelectedFileContent(data.content);
setSelectedFileContentType(data.contentType || "text/plain");
} else {
setSelectedFileContent(`Error loading file: ${data.error}`);
}
} catch (e: any) {
setSelectedFileContent(`Failed to read file from cloud storage: ${e.message}`);
} finally {
setLoadingFileContent(false);
}
};
const handleTestHfConnection = async () => {
setHfTestLoading(true);
setHfTestResult(null);
try {
const res = await fetch("/api/aura-store/hf-test-connection", {
method: "POST",
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
body: JSON.stringify({
repo: customHfRepo.trim(),
token: customHfToken.trim()
})
});
const data = await res.json();
setHfTestResult(data);
} catch (e: any) {
setHfTestResult({ success: false, message: e.message || "Failed to test connection." });
} finally {
setHfTestLoading(false);
}
};
useEffect(() => {
if (isOpen && currentUser) {
fetchItems();
fetchUsage();
fetchHfStatus();
if (activeTab === "dataai_explorer") fetchDataaiExplorer();
if (activeTab === "my_requests") fetchMyRequests();
if (activeTab === "admin_console" && isHarshaAdmin) fetchAdminData();
}
}, [isOpen, currentUser, activeTab]);
if (!isOpen || !currentUser) return null;
// Single Item Delete Handler
const requestDeleteSingle = (item: StoredItem) => {
setConfirmDeleteModal({ itemsToDelete: [item] });
};
// Execute Deletion
const executeDelete = async () => {
if (!confirmDeleteModal || confirmDeleteModal.itemsToDelete.length === 0) return;
const targets = confirmDeleteModal.itemsToDelete;
setConfirmDeleteModal(null);
if (targets.length === 1) {
const item = targets[0];
setDeletingId(item.id);
try {
const res = await fetch("/api/aura-store/delete-item", {
method: "POST",
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
body: JSON.stringify({
username: currentUser.username,
folder: item.folder,
fileName: item.name
})
});
const data = await res.json();
if (data.success) {
setActionMessage(`Deleted ${item.name}.`);
setItems(prev => prev.filter(i => i.id !== item.id));
setSelectedItemIds(prev => {
const next = new Set(prev);
next.delete(item.id);
return next;
});
onRefreshUserStats();
fetchItems();
} else {
setActionMessage(data.message || "Failed to delete item.");
}
} catch (e: any) {
setActionMessage(e.message || "Delete request error.");
} finally {
setDeletingId(null);
setTimeout(() => setActionMessage(null), 4000);
}
} else {
setBulkDeleting(true);
let successCount = 0;
for (const item of targets) {
try {
const res = await fetch("/api/aura-store/delete-item", {
method: "POST",
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
body: JSON.stringify({
username: currentUser.username,
folder: item.folder,
fileName: item.name
})
});
const data = await res.json();
if (data.success) successCount++;
} catch (e) {}
}
setActionMessage(`Reclaimed space: Deleted ${successCount} of ${targets.length} files.`);
setBulkDeleting(false);
setSelectedItemIds(new Set());
onRefreshUserStats();
fetchItems();
setTimeout(() => setActionMessage(null), 4000);
}
};
// Hugging Face Pull / Push Handlers
const handleHfPull = async () => {
setConfirmHfModal(null);
setHfSyncing(true);
setActionMessage("Pulling fresh data from Hugging Face bucket...");
try {
const res = await fetch("/api/aura-store/hf-pull", {
method: "POST",
headers: { ...getAuthHeaders() }
});
const data = await res.json();
if (data.success) {
setActionMessage(`Synced ${data.filesDownloaded} files from Hugging Face bucket.`);
fetchItems();
fetchHfStatus();
fetchDataaiExplorer();
onRefreshUserStats();
} else {
setActionMessage(data.message || "Pull failed.");
}
} catch (e: any) {
setActionMessage(e.message || "Error pulling from HF.");
} finally {
setHfSyncing(false);
setTimeout(() => setActionMessage(null), 5000);
}
};
const handleHfPush = async () => {
setConfirmHfModal(null);
setHfSyncing(true);
setActionMessage("Backing up all /dataai files to Hugging Face bucket...");
try {
const res = await fetch("/api/aura-store/hf-push", {
method: "POST",
headers: { ...getAuthHeaders() }
});
const data = await res.json();
if (data.success) {
setActionMessage(`Backup complete: ${data.filesUploaded} files pushed to HF bucket.`);
fetchHfStatus();
fetchDataaiExplorer();
} else {
setActionMessage(data.message || "Push failed.");
}
} catch (e: any) {
setActionMessage(e.message || "Error pushing to HF.");
} finally {
setHfSyncing(false);
setTimeout(() => setActionMessage(null), 5000);
}
};
const handleSaveHfConfig = async () => {
try {
const res = await fetch("/api/aura-store/hf-config", {
method: "POST",
headers: { "Content-Type": "application/json", ...getAuthHeaders() },
body: JSON.stringify({
repo: customHfRepo.trim(),
token: customHfToken.trim(),
autoSync: true
})
});
const data = await res.json();
if (data.success) {
setShowHfTokenModal(false);
fetchHfStatus();
fetchDataaiExplorer();
setActionMessage("Hugging Face dataset/bucket connection updated.");
setTimeout(() => setActionMessage(null), 4000);
} else {
setActionMessage(data.message || "Failed to update HF configuration.");
}
} catch (e: any) {
setActionMessage(e.message || "Failed to update HF configuration.");
}
};
// Submit UPI Upgrade Transaction
const handleSubmitUpiTransaction = async () => {
if (!upgradeModalTier || !transactionIdInput.trim()) {
alert("Please enter a valid 12-digit UPI Transaction ID or UTR number.");
return;
}
setIsSubmittingTxn(true);
try {
const tierConfig = TIER_CONFIGS[upgradeModalTier];
const res = await fetch("/api/upi/submit-transaction", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: currentUser.username,
targetTier: upgradeModalTier,
amount: tierConfig.priceINR,
transactionId: transactionIdInput.trim(),
phoneNumber: "9500076449",
note: `Payment for ${tierConfig.name} on Google Pay (9500076449)`
})
});
const data = await res.json();
if (data.success) {
setPaymentSuccessMsg(`Upgrade request submitted! Txn ID: ${transactionIdInput.trim()}. Admin (Harsha) will verify and activate your tier.`);
setTransactionIdInput("");
fetchUsage();
fetchMyRequests();
setTimeout(() => {
setPaymentSuccessMsg(null);
setUpgradeModalTier(null);
}, 4000);
} else {
alert(data.error || "Failed to submit transaction.");
}
} catch (e: any) {
alert(e.message || "Error submitting transaction.");
} finally {
setIsSubmittingTxn(false);
}
};
// Razorpay Checkout Handler
const handleRazorpayPayment = async (tierKey: SubscriptionTier) => {
const tierConfig = TIER_CONFIGS[tierKey];
try {
const orderRes = await fetch("/api/razorpay/create-order", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: currentUser.username,
tier: tierKey,
amount: tierConfig.priceINR * 100 // paise
})
});
const orderData = await orderRes.json();
if (!orderData.success) {
alert(orderData.error || "Failed to create payment order.");
return;
}
// Simulate or trigger Razorpay Checkout
const verifyRes = await fetch("/api/razorpay/verify-payment", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
username: currentUser.username,
targetTier: tierKey,
order_id: orderData.orderId,
payment_id: `rzp_pay_${Date.now()}`
})
});
const verifyData = await verifyRes.json();
if (verifyData.success) {
setPaymentSuccessMsg(`Instant Upgrade Verified! Your account is now ${tierConfig.name}.`);
onRefreshUserStats();
fetchUsage();
setTimeout(() => {
setPaymentSuccessMsg(null);
setUpgradeModalTier(null);
}, 3000);
}
} catch (e: any) {
alert(e.message || "Payment verification failed.");
}
};
// Admin: Approve Request
const handleAdminApprove = async (requestId: string) => {
try {
const res = await fetch("/api/subscriptions/admin-approve", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
requestId,
adminUsername: currentUser.username
})
});
const data = await res.json();
if (data.success) {
setActionMessage(data.message || "Request approved and tier updated!");
fetchAdminData();
onRefreshUserStats();
setTimeout(() => setActionMessage(null), 4000);
} else {
alert(data.error || "Failed to approve request.");
}
} catch (e: any) {
alert(e.message || "Approval error.");
}
};
// Admin: Decline Request
const handleAdminDecline = async (requestId: string) => {
try {
const res = await fetch("/api/subscriptions/admin-decline", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
requestId,
adminUsername: currentUser.username
})
});
const data = await res.json();
if (data.success) {
setActionMessage(data.message || "Request declined.");
fetchAdminData();
setTimeout(() => setActionMessage(null), 4000);
}
} catch (e: any) {
alert(e.message || "Decline error.");
}
};
// Admin: Directly Set User Tier
const handleAdminSetTier = async (targetUsername: string, newTier: string) => {
try {
const res = await fetch("/api/subscriptions/admin-set-tier", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
targetUsername,
tier: newTier,
days: 30,
adminUsername: currentUser.username
})
});
const data = await res.json();
if (data.success) {
setActionMessage(`Updated ${targetUsername} to ${newTier.toUpperCase()} (30 days).`);
setChangingTierUser(null);
fetchAdminData();
setTimeout(() => setActionMessage(null), 4000);
}
} catch (e: any) {
alert(e.message || "Failed to change user tier.");
}
};
// Copy helpers
const handleCopyGpayNumber = () => {
navigator.clipboard.writeText("9500076449");
setCopiedGpay(true);
setTimeout(() => setCopiedGpay(false), 2500);
};
const handleCopyUpiId = () => {
navigator.clipboard.writeText("9500076449@okaxis");
setCopiedUpiId(true);
setTimeout(() => setCopiedUpiId(false), 2500);
};
// Storage Filtering
const filteredItems = items.filter(item => {
const matchesFolder = activeFolder === "all" || item.folder === activeFolder;
const matchesSearch = searchQuery === "" || item.name.toLowerCase().includes(searchQuery.toLowerCase());
return matchesFolder && matchesSearch;
});
const filteredDataaiFiles = dataaiFiles.filter(file => {
const matchesSearch = !dataaiSearch.trim() ||
file.name.toLowerCase().includes(dataaiSearch.toLowerCase()) ||
file.relativePath.toLowerCase().includes(dataaiSearch.toLowerCase());
if (!matchesSearch) return false;
if (dataaiFilter === "all") return true;
return file.category === dataaiFilter;
});
const activeTierKey = (currentUser.tier || stats?.tier || "free").toLowerCase();
const currentTierConfig = TIER_CONFIGS[activeTierKey as SubscriptionTier] || TIER_CONFIGS.free;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/80 backdrop-blur-md animate-fade-in">
<div
className="bg-slate-900 border border-slate-700/80 rounded-3xl w-full max-w-5xl max-h-[92vh] flex flex-col shadow-2xl overflow-hidden text-slate-100 relative"
onClick={e => e.stopPropagation()}
>
{/* Top Header */}
<div className="px-6 py-4 border-b border-slate-800 bg-slate-950/80 flex flex-wrap items-center justify-between gap-4">
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-2xl bg-indigo-500/10 border border-indigo-500/30 flex items-center justify-center text-indigo-400 font-bold">
<User className="h-5 w-5" />
</div>
<div>
<div className="flex items-center gap-2">
<h3 className="font-bold text-base text-white">@{currentUser.username}</h3>
<span className={`px-2.5 py-0.5 rounded-full text-[11px] font-black uppercase tracking-wider ${
isHarshaAdmin ? "bg-purple-500/20 text-purple-300 border border-purple-500/40" :
activeTierKey === "auramax" ? "bg-amber-500/20 text-amber-300 border border-amber-500/40" :
activeTierKey === "aurapro" ? "bg-blue-500/20 text-blue-300 border border-blue-500/40" :
activeTierKey === "auraplus" ? "bg-emerald-500/20 text-emerald-300 border border-emerald-500/40" :
"bg-slate-800 text-slate-300 border border-slate-700"
}`}>
{isHarshaAdmin ? "Admin Master" : currentTierConfig.name}
</span>
{stats?.daysRemaining !== undefined && !isHarshaAdmin && activeTierKey !== "free" && (
<span className="text-[10px] text-amber-400 font-mono bg-amber-500/10 px-2 py-0.5 rounded-md border border-amber-500/20">
{stats.daysRemaining} days left
</span>
)}
</div>
<p className="text-xs text-slate-400">
{stats?.usedFormatted || "0 MB"} used of {stats?.maxFormatted || currentTierConfig.storageFormatted} Cloud Storage
</p>
</div>
</div>
{/* Navigation Bar */}
<div className="flex items-center gap-1.5 p-1 bg-slate-900 rounded-2xl border border-slate-800">
<button
onClick={() => setActiveTab("subscriptions")}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition flex items-center gap-1.5 cursor-pointer ${
activeTab === "subscriptions" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200"
}`}
>
<Award className="h-3.5 w-3.5" />
<span>Plans & Quotas</span>
</button>
<button
onClick={() => setActiveTab("storage")}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition flex items-center gap-1.5 cursor-pointer ${
activeTab === "storage" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200"
}`}
>
<HardDrive className="h-3.5 w-3.5" />
<span>Cloud Storage</span>
</button>
<button
onClick={() => {
setActiveTab("dataai_explorer");
fetchDataaiExplorer();
}}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition flex items-center gap-1.5 cursor-pointer ${
activeTab === "dataai_explorer" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200"
}`}
title={`View your cloud storage files in /dataai/${currentUser.username}/`}
>
<FolderTree className="h-3.5 w-3.5 text-indigo-400" />
<span>My Cloud Files</span>
</button>
<button
onClick={() => setActiveTab("my_requests")}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition flex items-center gap-1.5 cursor-pointer ${
activeTab === "my_requests" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200"
}`}
>
<CreditCard className="h-3.5 w-3.5" />
<span>Upgrade Status</span>
</button>
<button
onClick={() => setActiveTab("settings")}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition flex items-center gap-1.5 cursor-pointer ${
activeTab === "settings" ? "bg-indigo-600 text-white shadow-md" : "text-slate-400 hover:text-slate-200"
}`}
title="Aura Voice, Account Password & Theme Settings"
>
<Settings className="h-3.5 w-3.5" />
<span>Settings</span>
</button>
{isHarshaAdmin && (
<button
onClick={() => setActiveTab("admin_console")}
className={`px-3.5 py-1.5 rounded-xl text-xs font-bold transition flex items-center gap-1.5 cursor-pointer ${
activeTab === "admin_console" ? "bg-purple-600 text-white shadow-md" : "text-purple-400 hover:text-purple-200"
}`}
>
<ShieldCheck className="h-3.5 w-3.5" />
<span>Admin Console</span>
</button>
)}
</div>
<div className="flex items-center gap-2">
<button
onClick={onSignOut}
className="px-3 py-1.5 rounded-xl bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 text-xs font-bold flex items-center gap-1.5 cursor-pointer transition"
>
<LogOut className="h-3.5 w-3.5" />
<span>Sign Out</span>
</button>
<button
onClick={onClose}
className="h-8 w-8 rounded-full bg-slate-800 hover:bg-slate-700 text-slate-300 flex items-center justify-center cursor-pointer transition"
>
<X className="h-4 w-4" />
</button>
</div>
</div>
{/* Global Notification Banner */}
{actionMessage && (
<div className="bg-indigo-600/90 text-white text-xs px-6 py-2 flex items-center justify-between font-medium animate-fade-in">
<span>{actionMessage}</span>
<button onClick={() => setActionMessage(null)} className="text-white/80 hover:text-white">
<X className="h-3.5 w-3.5" />
</button>
</div>
)}
{/* TAB 1: SUBSCRIPTIONS & QUOTAS */}
{activeTab === "subscriptions" && (
<div className="flex-1 overflow-y-auto p-6 space-y-6 custom-scrollbar">
{/* Live Daily Quotas Tracker */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<Clock className="h-4 w-4 text-indigo-400" />
<h4 className="text-sm font-bold text-white">Today's Daily Quotas & Usage</h4>
<span className="text-[10px] text-slate-400 font-mono">(Resets daily at 00:00 UTC)</span>
</div>
<button
onClick={fetchUsage}
disabled={loadingUsage}
className="text-xs text-indigo-400 hover:text-indigo-300 flex items-center gap-1 font-semibold"
>
<RefreshCw className={`h-3 w-3 ${loadingUsage ? "animate-spin" : ""}`} />
<span>Refresh Meters</span>
</button>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-3">
{/* Chat Responses */}
<div className="bg-slate-900/80 p-3.5 rounded-xl border border-slate-800 flex flex-col justify-between">
<div className="flex items-center justify-between text-slate-400 text-xs mb-1">
<span className="flex items-center gap-1"><MessageSquare className="h-3 w-3 text-blue-400" /> Chat</span>
</div>
<div className="text-lg font-black text-white">
{usageData?.usage?.chatResponses || 0}
<span className="text-xs text-slate-400 font-normal"> / {isHarshaAdmin ? "∞" : currentTierConfig.chatResponsesPerDay}</span>
</div>
<div className="w-full bg-slate-800 h-1.5 rounded-full mt-2 overflow-hidden">
<div
className="bg-blue-500 h-full rounded-full"
style={{ width: `${Math.min(100, ((usageData?.usage?.chatResponses || 0) / (currentTierConfig.chatResponsesPerDay || 1)) * 100)}%` }}
/>
</div>
</div>
{/* AI Images */}
<div className="bg-slate-900/80 p-3.5 rounded-xl border border-slate-800 flex flex-col justify-between">
<div className="flex items-center justify-between text-slate-400 text-xs mb-1">
<span className="flex items-center gap-1"><ImageIcon className="h-3 w-3 text-pink-400" /> Images</span>
</div>
<div className="text-lg font-black text-white">
{usageData?.usage?.images || 0}
<span className="text-xs text-slate-400 font-normal"> / {isHarshaAdmin ? "∞" : currentTierConfig.imageGenerationsPerDay}</span>
</div>
<div className="w-full bg-slate-800 h-1.5 rounded-full mt-2 overflow-hidden">
<div
className="bg-pink-500 h-full rounded-full"
style={{ width: `${Math.min(100, ((usageData?.usage?.images || 0) / (currentTierConfig.imageGenerationsPerDay || 1)) * 100)}%` }}
/>
</div>
</div>
{/* AI Music */}
<div className="bg-slate-900/80 p-3.5 rounded-xl border border-slate-800 flex flex-col justify-between">
<div className="flex items-center justify-between text-slate-400 text-xs mb-1">
<span className="flex items-center gap-1"><Sparkles className="h-3 w-3 text-amber-400" /> Music</span>
</div>
<div className="text-lg font-black text-white">
{usageData?.usage?.music || 0}
<span className="text-xs text-slate-400 font-normal"> / {isHarshaAdmin ? "∞" : currentTierConfig.musicGenerationsPerDay}</span>
</div>
<div className="w-full bg-slate-800 h-1.5 rounded-full mt-2 overflow-hidden">
<div
className="bg-amber-500 h-full rounded-full"
style={{ width: `${Math.min(100, ((usageData?.usage?.music || 0) / (currentTierConfig.musicGenerationsPerDay || 1)) * 100)}%` }}
/>
</div>
</div>
{/* Documents */}
<div className="bg-slate-900/80 p-3.5 rounded-xl border border-slate-800 flex flex-col justify-between">
<div className="flex items-center justify-between text-slate-400 text-xs mb-1">
<span className="flex items-center gap-1"><FileText className="h-3 w-3 text-emerald-400" /> Docs</span>
</div>
<div className="text-lg font-black text-white">
{usageData?.usage?.documents || 0}
<span className="text-xs text-slate-400 font-normal"> / {isHarshaAdmin ? "∞" : currentTierConfig.documentsPerDay}</span>
</div>
<div className="w-full bg-slate-800 h-1.5 rounded-full mt-2 overflow-hidden">
<div
className="bg-emerald-500 h-full rounded-full"
style={{ width: `${Math.min(100, ((usageData?.usage?.documents || 0) / (currentTierConfig.documentsPerDay || 1)) * 100)}%` }}
/>
</div>
</div>
{/* Aura Codex */}
<div className="bg-slate-900/80 p-3.5 rounded-xl border border-slate-800 flex flex-col justify-between">
<div className="flex items-center justify-between text-slate-400 text-xs mb-1">
<span className="flex items-center gap-1"><Code2 className="h-3 w-3 text-purple-400" /> Codex</span>
</div>
<div className="text-lg font-black text-white">
{usageData?.usage?.codexMessages || 0}
<span className="text-xs text-slate-400 font-normal"> / {isHarshaAdmin ? "∞" : currentTierConfig.auraCodexPerDay}</span>
</div>
<div className="w-full bg-slate-800 h-1.5 rounded-full mt-2 overflow-hidden">
<div
className="bg-purple-500 h-full rounded-full"
style={{ width: `${Math.min(100, ((usageData?.usage?.codexMessages || 0) / (currentTierConfig.auraCodexPerDay || 1)) * 100)}%` }}
/>
</div>
</div>
{/* Cloud Storage */}
<div className="bg-slate-900/80 p-3.5 rounded-xl border border-slate-800 flex flex-col justify-between">
<div className="flex items-center justify-between text-slate-400 text-xs mb-1">
<span className="flex items-center gap-1"><HardDrive className="h-3 w-3 text-cyan-400" /> Storage</span>
</div>
<div className="text-sm font-black text-white">
{stats?.usedFormatted || "0 MB"}
<span className="text-[11px] text-slate-400 font-normal"> / {stats?.maxFormatted || currentTierConfig.storageFormatted}</span>
</div>
<div className="w-full bg-slate-800 h-1.5 rounded-full mt-2 overflow-hidden">
<div
className="bg-cyan-500 h-full rounded-full"
style={{ width: `${Math.min(100, stats?.percentageUsed || 0)}%` }}
/>
</div>
</div>
</div>
</div>
{/* Subscription Tiers Pricing Grid */}
<div>
<div className="text-center max-w-xl mx-auto mb-6">
<h3 className="text-xl font-black text-white">Upgrade Your Aura AI Membership</h3>
<p className="text-xs text-slate-400 mt-1">
Unlock more daily responses, higher cloud storage, fast model fleets, and creative studios. Powered by Google Pay (9500076449) & Razorpay.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{/* 1. Free Signed-In */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800 flex flex-col justify-between relative">
<div>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-slate-400 uppercase tracking-wider">Free Account</span>
</div>
<div className="text-2xl font-black text-white mb-3">
₹0 <span className="text-xs font-normal text-slate-400">/ forever</span>
</div>
<ul className="space-y-2 text-xs text-slate-300">
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> 1.00 GB Cloud Storage</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> 50 Chat responses/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> 15 Image gens/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> 30 Music tracks/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> 50 Codex messages/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> Gemini 2.5, 3.5, 3.6 & GPT-5.4/5.5</li>
</ul>
</div>
<div className="mt-6">
{activeTierKey === "free" ? (
<div className="w-full py-2 text-center rounded-xl bg-slate-800 text-slate-300 font-bold text-xs">
Current Active Plan
</div>
) : (
<div className="w-full py-2 text-center rounded-xl bg-slate-900 text-slate-400 text-xs">
Included Default
</div>
)}
</div>
</div>
{/* 2. AuraPlus (₹20/mo) */}
<div className={`bg-slate-950 p-5 rounded-2xl border flex flex-col justify-between relative ${
activeTierKey === "auraplus" ? "border-emerald-500/60 shadow-emerald-500/10 shadow-xl" : "border-slate-800 hover:border-emerald-500/40"
}`}>
<div>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-emerald-400 uppercase tracking-wider flex items-center gap-1">
<Zap className="h-3.5 w-3.5" /> AuraPlus
</span>
<span className="px-2 py-0.5 rounded-full bg-emerald-500/10 text-emerald-300 text-[10px] font-bold border border-emerald-500/30">
Popular
</span>
</div>
<div className="text-2xl font-black text-white mb-3">
₹20 <span className="text-xs font-normal text-slate-400">/ month</span>
</div>
<ul className="space-y-2 text-xs text-slate-300">
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> <strong>2.00 GB</strong> Cloud Storage</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> <strong>70</strong> Chat responses/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> <strong>20</strong> Image gens/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> <strong>50</strong> Music tracks/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> <strong>100</strong> Codex messages/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> All Gemini Models + GPT-5.6 Luna</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-emerald-400 shrink-0" /> Auto-tune to index.html view</li>
</ul>
</div>
<div className="mt-6">
{activeTierKey === "auraplus" ? (
<div className="w-full py-2 text-center rounded-xl bg-emerald-600 text-white font-bold text-xs shadow-md">
Active Plan
</div>
) : (
<button
onClick={() => setUpgradeModalTier("auraplus")}
className="w-full py-2 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs cursor-pointer shadow-md transition"
>
Upgrade (₹20/mo)
</button>
)}
</div>
</div>
{/* 3. AuraPro (₹50/mo) */}
<div className={`bg-slate-950 p-5 rounded-2xl border flex flex-col justify-between relative ${
activeTierKey === "aurapro" ? "border-blue-500/60 shadow-blue-500/10 shadow-xl" : "border-slate-800 hover:border-blue-500/40"
}`}>
<div>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-blue-400 uppercase tracking-wider flex items-center gap-1">
<Sparkles className="h-3.5 w-3.5" /> AuraPro
</span>
</div>
<div className="text-2xl font-black text-white mb-3">
₹50 <span className="text-xs font-normal text-slate-400">/ month</span>
</div>
<ul className="space-y-2 text-xs text-slate-300">
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> <strong>3.00 GB</strong> Cloud Storage</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> <strong>100</strong> Chat responses/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> <strong>30</strong> Image gens/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> <strong>60</strong> Music tracks/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> <strong>200</strong> Codex messages/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> GPT-5.6 Terra & Qwen 2.5 Coder</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-blue-400 shrink-0" /> Computer-Use Terminal Studio</li>
</ul>
</div>
<div className="mt-6">
{activeTierKey === "aurapro" ? (
<div className="w-full py-2 text-center rounded-xl bg-blue-600 text-white font-bold text-xs shadow-md">
Active Plan
</div>
) : (
<button
onClick={() => setUpgradeModalTier("aurapro")}
className="w-full py-2 rounded-xl bg-blue-600 hover:bg-blue-500 text-white font-bold text-xs cursor-pointer shadow-md transition"
>
Upgrade (₹50/mo)
</button>
)}
</div>
</div>
{/* 4. AuraMax (₹100/mo) */}
<div className={`bg-slate-950 p-5 rounded-2xl border flex flex-col justify-between relative ${
activeTierKey === "auramax" ? "border-amber-500/60 shadow-amber-500/10 shadow-xl" : "border-slate-800 hover:border-amber-500/40"
}`}>
<div>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-amber-400 uppercase tracking-wider flex items-center gap-1">
<Crown className="h-3.5 w-3.5" /> AuraMax
</span>
<span className="px-2 py-0.5 rounded-full bg-amber-500/10 text-amber-300 text-[10px] font-bold border border-amber-500/30">
Ultimate
</span>
</div>
<div className="text-2xl font-black text-white mb-3">
₹100 <span className="text-xs font-normal text-slate-400">/ month</span>
</div>
<ul className="space-y-2 text-xs text-slate-300">
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> <strong>5.00 GB</strong> Cloud Storage</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> <strong>200</strong> Chat responses/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> <strong>50</strong> Image gens/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> <strong>100</strong> Music tracks/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> <strong>300</strong> Codex messages/day</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> GPT-5.6 Sol + Full Model Fleet</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 shrink-0" /> Custom GPT Creator & Store</li>
</ul>
</div>
<div className="mt-6">
{activeTierKey === "auramax" ? (
<div className="w-full py-2 text-center rounded-xl bg-amber-600 text-white font-bold text-xs shadow-md">
Active Plan
</div>
) : (
<button
onClick={() => setUpgradeModalTier("auramax")}
className="w-full py-2 rounded-xl bg-amber-600 hover:bg-amber-500 text-white font-bold text-xs cursor-pointer shadow-md transition"
>
Upgrade (₹100/mo)
</button>
)}
</div>
</div>
</div>
</div>
</div>
)}
{/* TAB 2: CLOUD STORAGE & FILE MANAGER */}
{activeTab === "storage" && (
<div className="flex-1 flex flex-col md:flex-row overflow-hidden">
{/* Sidebar Navigation */}
<div className="w-full md:w-64 border-b md:border-b-0 md:border-r border-slate-800 p-4 space-y-4 bg-slate-950/60 shrink-0">
<div className="space-y-1">
<span className="text-[10px] font-bold uppercase tracking-wider text-slate-400 px-3">Folders (/dataai)</span>
<button
onClick={() => { setActiveFolder("all"); setSelectedItemIds(new Set()); }}
className={`w-full flex items-center justify-between px-3 py-2 rounded-xl text-xs font-semibold transition cursor-pointer ${
activeFolder === "all" ? "bg-indigo-600 text-white shadow-sm" : "text-slate-300 hover:bg-slate-800/60"
}`}
>
<div className="flex items-center gap-2">
<Folder className="h-4 w-4" />
<span>All User Files</span>
</div>
<span className="text-[10px] bg-slate-800/80 px-2 py-0.5 rounded-full text-slate-300 font-mono">
{items.length}
</span>
</button>
<button
onClick={() => { setActiveFolder("media storage"); setSelectedItemIds(new Set()); }}
className={`w-full flex items-center justify-between px-3 py-2 rounded-xl text-xs font-semibold transition cursor-pointer ${
activeFolder === "media storage" ? "bg-indigo-600 text-white shadow-sm" : "text-slate-300 hover:bg-slate-800/60"
}`}
>
<div className="flex items-center gap-2">
<HardDrive className="h-4 w-4 text-emerald-400" />
<span>media storage</span>
</div>
<span className="text-[10px] bg-slate-800/80 px-2 py-0.5 rounded-full text-slate-300 font-mono">
{items.filter(i => i.folder === "media storage").length}
</span>
</button>
<button
onClick={() => { setActiveFolder("chats"); setSelectedItemIds(new Set()); }}
className={`w-full flex items-center justify-between px-3 py-2 rounded-xl text-xs font-semibold transition cursor-pointer ${
activeFolder === "chats" ? "bg-indigo-600 text-white shadow-sm" : "text-slate-300 hover:bg-slate-800/60"
}`}
>
<div className="flex items-center gap-2">
<MessageSquare className="h-4 w-4 text-blue-400" />
<span>chats</span>
</div>
<span className="text-[10px] bg-slate-800/80 px-2 py-0.5 rounded-full text-slate-300 font-mono">
{items.filter(i => i.folder === "chats").length}
</span>
</button>
<button
onClick={() => { setActiveFolder("generated media"); setSelectedItemIds(new Set()); }}
className={`w-full flex items-center justify-between px-3 py-2 rounded-xl text-xs font-semibold transition cursor-pointer ${
activeFolder === "generated media" ? "bg-indigo-600 text-white shadow-sm" : "text-slate-300 hover:bg-slate-800/60"
}`}
>
<div className="flex items-center gap-2">
<Sparkles className="h-4 w-4 text-amber-400" />
<span>generated media</span>
</div>
<span className="text-[10px] bg-slate-800/80 px-2 py-0.5 rounded-full text-slate-300 font-mono">
{items.filter(i => i.folder === "generated media").length}
</span>
</button>
<button
onClick={() => { setActiveFolder("pinned chats"); setSelectedItemIds(new Set()); }}
className={`w-full flex items-center justify-between px-3 py-2 rounded-xl text-xs font-semibold transition cursor-pointer ${
activeFolder === "pinned chats" ? "bg-indigo-600 text-white shadow-sm" : "text-slate-300 hover:bg-slate-800/60"
}`}
>
<div className="flex items-center gap-2">
<Pin className="h-4 w-4 text-purple-400" />
<span>pinned chats</span>
</div>
<span className="text-[10px] bg-slate-800/80 px-2 py-0.5 rounded-full text-slate-300 font-mono">
{items.filter(i => i.folder === "pinned chats").length}
</span>
</button>
</div>
{/* User Private Storage Volume Banner */}
<div className="p-3 bg-slate-900 rounded-2xl border border-slate-800 space-y-2 text-xs">
<div className="flex items-center justify-between">
<div className="flex items-center gap-1.5 font-bold text-white text-[11px]">
<HardDrive className="h-3.5 w-3.5 text-indigo-400" />
<span>My Cloud Volume</span>
</div>
<span className="px-1.5 py-0.5 rounded text-[9px] font-mono bg-emerald-500/10 text-emerald-300 border border-emerald-500/30">
Private
</span>
</div>
<p className="text-[10px] text-slate-400 leading-tight font-mono truncate">
/dataai/{currentUser.username}/
</p>
<div className="flex flex-col gap-1.5 pt-1">
<button
onClick={() => {
setActiveTab("dataai_explorer");
fetchDataaiExplorer();
}}
className="w-full py-1.5 px-2 rounded-lg bg-indigo-600/20 hover:bg-indigo-600/30 text-indigo-300 font-semibold text-[10px] flex items-center justify-center gap-1.5 cursor-pointer border border-indigo-500/30 transition"
title={`Explore files stored in /dataai/${currentUser.username}/`}
>
<FolderTree className="h-3.5 w-3.5 text-indigo-400" />
<span>Open My Cloud Explorer</span>
</button>
{isHarshaAdmin && (
<div className="flex gap-1 pt-1 border-t border-slate-800">
<button
onClick={() => setConfirmHfModal({ action: "push" })}
disabled={hfSyncing}
className="flex-1 py-1 px-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold text-[9px] flex items-center justify-center gap-1 cursor-pointer"
title="Admin: Push all files to HF Bucket"
>
<UploadCloud className="h-2.5 w-2.5 text-emerald-400" />
<span>HF Push</span>
</button>
<button
onClick={() => setConfirmHfModal({ action: "pull" })}
disabled={hfSyncing}
className="flex-1 py-1 px-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold text-[9px] flex items-center justify-center gap-1 cursor-pointer"
title="Admin: Pull files from HF Bucket"
>
<Download className="h-2.5 w-2.5 text-amber-400" />
<span>HF Pull</span>
</button>
<button
onClick={() => {
setCustomHfRepo(hfStatus?.repo || "");
setHfTestResult(null);
setShowHfTokenModal(true);
}}
className="py-1 px-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-400 font-semibold text-[9px] flex items-center justify-center cursor-pointer transition border border-slate-700"
title="Configure Master Bucket"
>
<Settings className="h-2.5 w-2.5" />
</button>
</div>
)}
</div>
</div>
</div>
{/* Main File Browser & Controls */}
<div className="flex-1 flex flex-col overflow-hidden bg-slate-900/40">
{/* Search & Actions Bar */}
<div className="p-4 border-b border-slate-800 flex flex-wrap items-center justify-between gap-3 bg-slate-950/40">
<div className="flex items-center gap-2 flex-1 min-w-[200px]">
<div className="relative w-full max-w-sm">
<Search className="h-3.5 w-3.5 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
<input
type="text"
placeholder="Search files or chats..."
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
className="w-full pl-8 pr-3 py-1.5 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white placeholder:text-slate-400 focus:outline-none focus:border-indigo-500"
/>
</div>
<button
onClick={fetchItems}
disabled={loading}
className="p-2 rounded-xl bg-slate-900 hover:bg-slate-800 text-slate-300 border border-slate-800 text-xs cursor-pointer"
title="Refresh List"
>
<RefreshCw className={`h-3.5 w-3.5 ${loading ? "animate-spin" : ""}`} />
</button>
</div>
{/* Bulk Actions */}
{selectedItemIds.size > 0 && (
<div className="flex items-center gap-2 bg-indigo-950/60 border border-indigo-500/30 px-3 py-1.5 rounded-xl animate-fade-in">
<span className="text-xs text-indigo-300 font-bold">{selectedItemIds.size} selected</span>
<button
onClick={() => {
const targets = items.filter(i => selectedItemIds.has(i.id));
setConfirmDeleteModal({ itemsToDelete: targets });
}}
className="px-2.5 py-1 rounded-lg bg-red-600 hover:bg-red-500 text-white font-bold text-xs flex items-center gap-1 cursor-pointer"
>
<Trash2 className="h-3 w-3" />
<span>Delete Selected</span>
</button>
</div>
)}
</div>
{/* Items Table / List */}
<div className="flex-1 overflow-y-auto p-4 space-y-2 custom-scrollbar">
{loading ? (
<div className="py-20 flex flex-col items-center justify-center text-slate-400 gap-2">
<RefreshCw className="h-6 w-6 animate-spin text-indigo-400" />
<span className="text-xs">Reading /dataai storage...</span>
</div>
) : filteredItems.length === 0 ? (
<div className="py-20 flex flex-col items-center justify-center text-slate-400 gap-2 text-center">
<Folder className="h-8 w-8 text-slate-600" />
<p className="text-sm font-bold text-slate-300">No items found in {activeFolder}</p>
<p className="text-xs text-slate-400">Chats, images, and audio will appear here automatically.</p>
</div>
) : (
<div className="space-y-1.5">
{filteredItems.map(item => {
const isSelected = selectedItemIds.has(item.id);
return (
<div
key={item.id}
className={`p-3 rounded-2xl border transition flex items-center justify-between gap-3 ${
isSelected ? "bg-indigo-950/40 border-indigo-500/50" : "bg-slate-950/70 border-slate-800/80 hover:border-slate-700"
}`}
>
<div className="flex items-center gap-3 min-w-0 flex-1">
<button
onClick={() => {
setSelectedItemIds(prev => {
const next = new Set(prev);
if (next.has(item.id)) next.delete(item.id);
else next.add(item.id);
return next;
});
}}
className="text-slate-400 hover:text-indigo-400 cursor-pointer"
>
{isSelected ? <CheckSquare className="h-4 w-4 text-indigo-400" /> : <Square className="h-4 w-4" />}
</button>
<div className="h-8 w-8 rounded-xl bg-slate-900 border border-slate-800 flex items-center justify-center shrink-0">
{item.folder === "chats" || item.folder === "pinned chats" ? (
<MessageSquare className="h-4 w-4 text-blue-400" />
) : (
<ImageIcon className="h-4 w-4 text-amber-400" />
)}
</div>
<div className="min-w-0 flex-1">
<p className="text-xs font-bold text-white truncate">{item.name}</p>
<div className="flex items-center gap-2 text-[10px] text-slate-400 font-mono mt-0.5">
<span className="bg-slate-900 px-1.5 py-0.5 rounded text-slate-300">{item.folder}</span>
<span>{item.sizeFormatted}</span>
<span>• {new Date(item.updatedAt).toLocaleDateString()}</span>
</div>
</div>
</div>
<div className="flex items-center gap-1.5">
{item.dataUrl && (
<button
onClick={() => setPreviewItem(item)}
className="p-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 text-xs cursor-pointer"
title="Preview"
>
<ExternalLink className="h-3.5 w-3.5" />
</button>
)}
<button
onClick={() => requestDeleteSingle(item)}
disabled={deletingId === item.id}
className="p-1.5 rounded-lg bg-red-500/10 hover:bg-red-500/20 text-red-400 text-xs cursor-pointer"
title="Delete Item"
>
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
</div>
);
})}
</div>
)}
</div>
</div>
</div>
)}
{/* TAB: MY CLOUD FILES (/dataai/{username}/) */}
{activeTab === "dataai_explorer" && (
<div className="flex-1 flex flex-col overflow-hidden bg-slate-950/60">
{/* Top Summary & Control Ribbon */}
<div className="p-4 border-b border-slate-800 bg-slate-900/80 flex flex-wrap items-center justify-between gap-4">
<div className="flex items-center gap-3 min-w-[280px]">
<div className="h-10 w-10 rounded-2xl bg-indigo-500/10 border border-indigo-500/30 flex items-center justify-center text-indigo-400 font-bold shrink-0">
<FolderTree className="h-5 w-5" />
</div>
<div>
<div className="flex items-center gap-2">
<h4 className="font-bold text-sm text-white">
{isHarshaAdmin ? "Master Cloud Directory (/dataai)" : `@${currentUser.username}'s Cloud Storage`}
</h4>
<span className="px-2 py-0.5 rounded-full text-[10px] font-bold bg-emerald-500/10 text-emerald-300 border border-emerald-500/30">
{isHarshaAdmin ? "Admin Master" : "User Isolated • Private"}
</span>
<span className="px-2 py-0.5 rounded-full text-[10px] font-bold bg-indigo-500/10 text-indigo-300 border border-indigo-500/30">
Persistent Cloud
</span>
</div>
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-slate-400 mt-0.5">
<span className="font-mono text-slate-300">
Path: <strong className="text-emerald-400">/dataai/{isHarshaAdmin ? "" : `${currentUser.username}/`}</strong>
</span>
<span>•</span>
<span>{dataaiFiles.length} private files ({formatBytes(dataaiFiles.reduce((acc, f) => acc + (f.sizeBytes || 0), 0))})</span>
</div>
</div>
</div>
<div className="flex flex-wrap items-center gap-2">
{isHarshaAdmin && (
<>
<button
onClick={() => setConfirmHfModal({ action: "push" })}
disabled={hfSyncing}
className="px-3 py-1.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs flex items-center gap-1.5 cursor-pointer shadow-sm transition disabled:opacity-50"
title="Admin: Upload all files from /dataai to Hugging Face Bucket"
>
<UploadCloud className="h-3.5 w-3.5" />
<span>Backup All</span>
</button>
<button
onClick={() => setConfirmHfModal({ action: "pull" })}
disabled={hfSyncing}
className="px-3 py-1.5 rounded-xl bg-amber-600 hover:bg-amber-500 text-white font-bold text-xs flex items-center gap-1.5 cursor-pointer shadow-sm transition disabled:opacity-50"
title="Admin: Download and restore files from Hugging Face Bucket"
>
<Download className="h-3.5 w-3.5" />
<span>Restore All</span>
</button>
<button
onClick={() => {
setCustomHfRepo(hfStatus?.repo || "");
setHfTestResult(null);
setShowHfTokenModal(true);
}}
className="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 border border-slate-700 font-bold text-xs flex items-center gap-1.5 cursor-pointer transition"
title="Configure Master Bucket"
>
<Settings className="h-3.5 w-3.5 text-amber-400" />
<span>Bucket Config</span>
</button>
</>
)}
<button
onClick={fetchDataaiExplorer}
disabled={loadingDataai}
className="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700 text-xs font-semibold flex items-center gap-1.5 cursor-pointer"
title="Refresh my cloud files"
>
<RefreshCw className={`h-3.5 w-3.5 ${loadingDataai ? "animate-spin" : ""}`} />
<span>Refresh</span>
</button>
</div>
</div>
{/* Explorer Split Panes */}
<div className="flex-1 flex flex-col md:flex-row overflow-hidden">
{/* Left Pane: File Directory */}
<div className="w-full md:w-80 lg:w-96 border-b md:border-b-0 md:border-r border-slate-800 flex flex-col bg-slate-950/50 shrink-0">
{/* Search & Filter Toolbar */}
<div className="p-3 border-b border-slate-800 space-y-2">
<div className="relative">
<Search className="h-3.5 w-3.5 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
<input
type="text"
placeholder={`Search in @${currentUser.username}...`}
value={dataaiSearch}
onChange={e => setDataaiSearch(e.target.value)}
className="w-full pl-8 pr-3 py-1.5 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white placeholder:text-slate-500 focus:outline-none focus:border-indigo-500"
/>
</div>
<div className="flex items-center gap-1 overflow-x-auto pb-1 text-[11px] custom-scrollbar">
{(["all", "chats", "media", "users", "config"] as const).map(cat => (
<button
key={cat}
onClick={() => setDataaiFilter(cat)}
className={`px-2.5 py-1 rounded-lg capitalize font-semibold transition cursor-pointer whitespace-nowrap ${
dataaiFilter === cat ? "bg-indigo-600 text-white shadow-sm" : "bg-slate-900 text-slate-400 hover:text-slate-200 border border-slate-800"
}`}
>
{cat === "users" ? "userData" : cat}
</button>
))}
</div>
</div>
{/* Files List */}
<div className="flex-1 overflow-y-auto p-2 space-y-1 custom-scrollbar">
{loadingDataai ? (
<div className="py-12 flex flex-col items-center justify-center text-slate-400 text-xs gap-2">
<RefreshCw className="h-5 w-5 animate-spin text-indigo-400" />
<span>Reading @{currentUser.username}'s cloud storage...</span>
</div>
) : filteredDataaiFiles.length === 0 ? (
<div className="py-12 text-center text-slate-500 text-xs px-4">
<Folder className="h-8 w-8 mx-auto mb-2 text-slate-600" />
<p>No files found in your cloud storage matching "{dataaiSearch}".</p>
<p className="text-[10px] text-slate-600 mt-1">Start chatting or saving media to populate your account folder.</p>
</div>
) : (
filteredDataaiFiles.map(file => {
const isSelected = selectedDataaiFile?.relativePath === file.relativePath;
return (
<button
key={file.relativePath}
onClick={() => handleSelectDataaiFile(file)}
className={`w-full text-left p-2.5 rounded-xl border transition cursor-pointer flex items-start gap-2.5 ${
isSelected
? "bg-indigo-600/15 border-indigo-500/50 text-white shadow-sm"
: "bg-slate-900/60 border-slate-800/80 text-slate-300 hover:bg-slate-900 hover:border-slate-700"
}`}
>
<div className="mt-0.5 shrink-0">
{file.category === "users" ? (
<User className="h-4 w-4 text-emerald-400" />
) : file.category === "chats" ? (
<MessageSquare className="h-4 w-4 text-indigo-400" />
) : file.category === "media" ? (
<ImageIcon className="h-4 w-4 text-pink-400" />
) : file.category === "config" ? (
<Settings className="h-4 w-4 text-cyan-400" />
) : (
<FileText className="h-4 w-4 text-slate-400" />
)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-1">
<span className="font-semibold text-xs text-white truncate">{file.name}</span>
<span className="text-[10px] font-mono text-slate-400 shrink-0">{file.sizeFormatted}</span>
</div>
<div className="flex items-center justify-between gap-1 text-[10px] text-slate-400 font-mono mt-0.5">
<span className="truncate text-slate-400">{file.relativePath}</span>
<span className="shrink-0 text-slate-500">{new Date(file.updatedAt).toLocaleDateString()}</span>
</div>
</div>
</button>
);
})
)}
</div>
</div>
{/* Right Pane: File Viewer & Inspector */}
<div className="flex-1 flex flex-col overflow-hidden bg-slate-950/80">
{selectedDataaiFile ? (
<>
{/* Viewer Header */}
<div className="p-3.5 border-b border-slate-800 bg-slate-900/70 flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2.5 min-w-0">
<div className="p-1.5 rounded-lg bg-slate-800 text-indigo-400 shrink-0">
<FileCode className="h-4 w-4" />
</div>
<div className="min-w-0">
<div className="flex items-center gap-2">
<h5 className="font-bold text-xs text-white font-mono truncate">/dataai/{selectedDataaiFile.relativePath}</h5>
<span className="px-1.5 py-0.5 rounded text-[9px] font-mono bg-emerald-500/10 text-emerald-300 border border-emerald-500/30 shrink-0">
Private File
</span>
</div>
<p className="text-[11px] text-slate-400 font-mono">
Size: {selectedDataaiFile.sizeFormatted} • Last Modified: {new Date(selectedDataaiFile.updatedAt).toLocaleString()}
</p>
</div>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => {
if (selectedFileContent) {
navigator.clipboard.writeText(selectedFileContent);
setActionMessage("Copied file content to clipboard.");
setTimeout(() => setActionMessage(null), 3000);
}
}}
className="px-2.5 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold text-xs flex items-center gap-1 cursor-pointer transition border border-slate-700"
title="Copy file content"
>
<Copy className="h-3 w-3" />
<span>Copy</span>
</button>
<button
onClick={() => {
if (selectedFileContent) {
const blob = new Blob([selectedFileContent], { type: selectedFileContentType });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = selectedDataaiFile.name;
a.click();
URL.revokeObjectURL(url);
}
}}
className="px-2.5 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold text-xs flex items-center gap-1 cursor-pointer transition border border-slate-700"
title="Download file to device"
>
<Download className="h-3 w-3" />
<span>Download</span>
</button>
</div>
</div>
{/* Viewer Content */}
<div className="flex-1 overflow-auto p-4 font-mono text-xs text-slate-200 bg-slate-950 custom-scrollbar">
{loadingFileContent ? (
<div className="py-20 flex flex-col items-center justify-center text-slate-400 gap-2">
<RefreshCw className="h-5 w-5 animate-spin text-indigo-400" />
<span>Reading /dataai/{selectedDataaiFile.relativePath}...</span>
</div>
) : selectedFileContentType.startsWith("image/") ? (
<div className="flex flex-col items-center justify-center p-6 space-y-4">
<img
src={`data:${selectedFileContentType};base64,${selectedFileContent}`}
alt={selectedDataaiFile.name}
className="max-h-96 max-w-full rounded-xl object-contain border border-slate-800 shadow-lg"
/>
<span className="text-xs text-slate-400">{selectedDataaiFile.name} ({selectedDataaiFile.sizeFormatted})</span>
</div>
) : selectedFileContentType.startsWith("audio/") ? (
<div className="flex flex-col items-center justify-center p-8 space-y-4">
<Volume2 className="h-12 w-12 text-purple-400" />
<audio
controls
src={`data:${selectedFileContentType};base64,${selectedFileContent}`}
className="w-full max-w-md"
/>
<span className="text-xs text-slate-400">{selectedDataaiFile.name}</span>
</div>
) : (
<pre className="whitespace-pre-wrap break-all leading-relaxed text-slate-300 font-mono text-[11px] bg-slate-900/60 p-4 rounded-xl border border-slate-800/80">
{selectedFileContent}
</pre>
)}
</div>
</>
) : (
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center text-slate-500 space-y-3">
<div className="h-14 w-14 rounded-2xl bg-slate-900 border border-slate-800 flex items-center justify-center text-slate-600">
<FolderTree className="h-7 w-7 text-indigo-400" />
</div>
<h5 className="font-bold text-sm text-slate-300">Select a file from your cloud folder</h5>
<p className="text-xs text-slate-500 max-w-sm">
Click any file on the left to inspect its contents, size, and download or copy securely from your isolated storage.
</p>
</div>
)}
</div>
</div>
</div>
)}
{/* TAB 3: MY UPGRADE REQUESTS */}
{activeTab === "my_requests" && (
<div className="flex-1 overflow-y-auto p-6 space-y-4 custom-scrollbar">
<div className="flex items-center justify-between">
<div>
<h4 className="text-base font-bold text-white">My Subscription Upgrade Requests</h4>
<p className="text-xs text-slate-400">Track payment status and manual approvals from Admin (Harsha).</p>
</div>
<button
onClick={fetchMyRequests}
disabled={loadingMyRequests}
className="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-semibold flex items-center gap-1.5"
>
<RefreshCw className={`h-3 w-3 ${loadingMyRequests ? "animate-spin" : ""}`} />
<span>Refresh Status</span>
</button>
</div>
{loadingMyRequests ? (
<div className="py-20 flex justify-center text-slate-400">
<RefreshCw className="h-6 w-6 animate-spin text-indigo-400" />
</div>
) : myRequests.length === 0 ? (
<div className="py-16 text-center bg-slate-950 rounded-2xl border border-slate-800 p-6">
<CreditCard className="h-10 w-10 text-slate-600 mx-auto mb-2" />
<p className="text-sm font-bold text-slate-300">No Upgrade Requests Yet</p>
<p className="text-xs text-slate-400 mt-1">Upgrade to AuraPlus (₹20), AuraPro (₹50), or AuraMax (₹100) from the Plans tab.</p>
<button
onClick={() => setActiveTab("subscriptions")}
className="mt-4 px-4 py-2 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs"
>
View Plans
</button>
</div>
) : (
<div className="space-y-3">
{myRequests.map(req => (
<div key={req.id} className="p-4 rounded-2xl bg-slate-950 border border-slate-800 flex items-center justify-between gap-4">
<div>
<div className="flex items-center gap-2">
<span className="font-bold text-white text-sm uppercase">{req.requestedTier}</span>
<span className="text-xs font-black text-indigo-400 font-mono">₹{req.amount}</span>
<span className={`px-2 py-0.5 rounded-full text-[10px] font-black uppercase tracking-wider ${
req.status === "approved" ? "bg-emerald-500/20 text-emerald-300 border border-emerald-500/40" :
req.status === "declined" ? "bg-red-500/20 text-red-300 border border-red-500/40" :
"bg-amber-500/20 text-amber-300 border border-amber-500/40"
}`}>
{req.status === "approved" ? "Approved & Activated" : req.status === "declined" ? "Declined" : "Pending Admin Review"}
</span>
</div>
<div className="text-xs text-slate-400 font-mono mt-1 space-x-3">
<span>Txn: {req.transactionId}</span>
<span>• Via: {req.paymentMethod === "google_pay" ? "Google Pay (9500076449)" : "Razorpay"}</span>
<span>• Date: {new Date(req.createdAt).toLocaleString()}</span>
</div>
{req.note && <p className="text-xs text-slate-300 mt-1 italic">"{req.note}"</p>}
</div>
</div>
))}
</div>
)}
</div>
)}
{/* TAB 4: ADMIN CONSOLE (HARSHA ONLY) */}
{activeTab === "admin_console" && isHarshaAdmin && (
<div className="flex-1 overflow-y-auto p-6 space-y-6 custom-scrollbar">
<div className="flex items-center justify-between border-b border-purple-500/30 pb-3">
<div className="flex items-center gap-2">
<ShieldCheck className="h-5 w-5 text-purple-400" />
<h4 className="text-base font-bold text-white">Harsha Master Admin Control Console</h4>
</div>
<button
onClick={fetchAdminData}
disabled={loadingAdminData}
className="px-3 py-1.5 rounded-xl bg-purple-600 hover:bg-purple-500 text-white text-xs font-bold flex items-center gap-1.5 cursor-pointer shadow-md"
>
<RefreshCw className={`h-3 w-3 ${loadingAdminData ? "animate-spin" : ""}`} />
<span>Refresh Console</span>
</button>
</div>
{/* Section A: Pending Upgrade Requests */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800 space-y-3">
<h5 className="text-sm font-bold text-amber-400 flex items-center gap-2">
<Clock className="h-4 w-4" /> Pending Payment Approvals ({adminRequests.filter(r => r.status === "pending").length})
</h5>
{adminRequests.filter(r => r.status === "pending").length === 0 ? (
<p className="text-xs text-slate-400 py-3">No pending upgrade requests. All user payments processed!</p>
) : (
<div className="space-y-2.5">
{adminRequests.filter(r => r.status === "pending").map(req => (
<div key={req.id} className="p-3.5 rounded-xl bg-slate-900 border border-slate-800 flex flex-wrap items-center justify-between gap-3">
<div>
<div className="flex items-center gap-2">
<strong className="text-white text-sm">@{req.username}</strong>
<span className="px-2 py-0.5 rounded-md bg-indigo-500/20 text-indigo-300 font-bold text-xs">
Upgrade to {req.requestedTier.toUpperCase()}
</span>
<span className="font-bold text-emerald-400 text-xs font-mono">₹{req.amount}</span>
</div>
<div className="text-xs text-slate-400 font-mono mt-1 space-x-2">
<span>Txn / UTR: <strong className="text-amber-300">{req.transactionId}</strong></span>
<span>• Phone: {req.phoneNumber || "9500076449"}</span>
<span>• {new Date(req.createdAt).toLocaleString()}</span>
</div>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => handleAdminApprove(req.id)}
className="px-3 py-1.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs flex items-center gap-1 cursor-pointer shadow-sm"
>
<CheckCircle2 className="h-3.5 w-3.5" />
<span>Approve & Activate</span>
</button>
<button
onClick={() => handleAdminDecline(req.id)}
className="px-3 py-1.5 rounded-xl bg-red-600/80 hover:bg-red-600 text-white font-bold text-xs cursor-pointer"
>
Decline
</button>
</div>
</div>
))}
</div>
)}
</div>
{/* Section B: Manage All Registered Users */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800 space-y-3">
<div className="flex items-center justify-between">
<h5 className="text-sm font-bold text-purple-400 flex items-center gap-2">
<Users className="h-4 w-4" /> Registered Users & Tiers ({adminUsers.length})
</h5>
<input
type="text"
placeholder="Filter users..."
value={adminUserSearch}
onChange={e => setAdminUserSearch(e.target.value)}
className="px-3 py-1 rounded-xl bg-slate-900 border border-slate-800 text-xs text-white placeholder:text-slate-400"
/>
</div>
<div className="space-y-2 max-h-[300px] overflow-y-auto custom-scrollbar">
{adminUsers
.filter(u => adminUserSearch === "" || u.username.toLowerCase().includes(adminUserSearch.toLowerCase()))
.map(u => (
<div key={u.id} className="p-3 rounded-xl bg-slate-900/90 border border-slate-800 flex items-center justify-between gap-3 text-xs">
<div>
<div className="flex items-center gap-2 font-bold text-white">
<span>@{u.username}</span>
<span className="px-2 py-0.5 rounded-md bg-slate-800 text-slate-300 font-mono text-[10px] uppercase">
{u.tier}
</span>
{u.daysRemaining !== undefined && (
<span className="text-[10px] text-amber-400 font-mono">({u.daysRemaining} days left)</span>
)}
</div>
<p className="text-slate-400 font-mono text-[10px] mt-0.5">
Storage: {u.usedFormatted} / {u.maxFormatted} • Joined: {new Date(u.createdAt).toLocaleDateString()}
</p>
</div>
<div className="flex items-center gap-2">
<select
defaultValue={u.tier}
onChange={e => handleAdminSetTier(u.username, e.target.value)}
className="bg-slate-950 border border-slate-700 rounded-lg px-2 py-1 text-xs text-white focus:outline-none"
>
<option value="free">Free</option>
<option value="auraplus">AuraPlus (₹20)</option>
<option value="aurapro">AuraPro (₹50)</option>
<option value="auramax">AuraMax (₹100)</option>
<option value="admin">Admin</option>
</select>
</div>
</div>
))}
</div>
</div>
</div>
)}
{/* TAB 5: SETTINGS */}
{activeTab === "settings" && (
<div className="flex-1 overflow-y-auto p-6 space-y-6 custom-scrollbar">
{/* Header Banner */}
<div className="bg-gradient-to-r from-indigo-950/80 via-slate-900 to-purple-950/80 p-5 rounded-2xl border border-indigo-500/20 shadow-xl flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
<div className="h-7 w-7 rounded-lg bg-indigo-600 flex items-center justify-center text-white font-bold">
<Settings className="h-4 w-4" />
</div>
<h3 className="text-base font-bold text-white">System, AI & Engine Settings</h3>
</div>
<p className="text-xs text-slate-300">
Configure cognitive accuracy, automatic day/night theme scheduling, voice synthesis, and connected Google AI features.
</p>
</div>
<div className="flex items-center gap-2">
<span className="text-[10px] font-mono font-bold px-2.5 py-1 rounded-lg bg-emerald-500/10 border border-emerald-500/30 text-emerald-300 flex items-center gap-1.5">
<Activity className="h-3 w-3 text-emerald-400" />
<span>Sub-Second Flash Active</span>
</span>
</div>
</div>
{/* Section 1: Accuracy Engine [99.999%] */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<ShieldCheck className="h-4 w-4 text-emerald-400" />
<h4 className="text-sm font-bold text-white">Accuracy Engine</h4>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-emerald-500/10 text-emerald-300 border border-emerald-500/20 font-bold">
{accuracyMode === "perfect" ? "99.999% Factual Precision" : accuracyMode === "fast" ? "Fast Burst" : "Creative Balanced"}
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed">
Controls factual rigor, hallucination suppression, and deterministic verification across all chat responses.
</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<button
type="button"
onClick={() => {
if (onSelectAccuracyMode) onSelectAccuracyMode("perfect");
if (onSelectUnderstandingMode) onSelectUnderstandingMode("ultra-precision");
}}
className={`p-4 rounded-xl border text-left transition cursor-pointer flex flex-col justify-between ${
accuracyMode === "perfect"
? "bg-emerald-950/40 border-emerald-500/80 ring-2 ring-emerald-500/30 text-white shadow-lg"
: "bg-slate-900/60 border-slate-800 hover:border-slate-700 text-slate-300"
}`}
>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-emerald-300 flex items-center gap-1.5">
<CheckCircle className="h-3.5 w-3.5" /> 99.999% Perfect Standard
</span>
{accuracyMode === "perfect" && <span className="h-2 w-2 rounded-full bg-emerald-400 animate-ping" />}
</div>
<p className="text-[11px] text-slate-400 leading-relaxed mb-2">
Zero fluff, deterministic 0.15 temperature, peer-reviewed accuracy, and exhaustive factual detail.
</p>
<span className="text-[9.5px] font-mono font-bold text-emerald-400 uppercase">
{accuracyMode === "perfect" ? "Active Standard" : "Select Standard"}
</span>
</button>
<button
type="button"
onClick={() => {
if (onSelectAccuracyMode) onSelectAccuracyMode("balanced");
if (onSelectUnderstandingMode) onSelectUnderstandingMode("grade-7");
}}
className={`p-4 rounded-xl border text-left transition cursor-pointer flex flex-col justify-between ${
accuracyMode === "balanced"
? "bg-indigo-950/40 border-indigo-500/80 ring-2 ring-indigo-500/30 text-white shadow-lg"
: "bg-slate-900/60 border-slate-800 hover:border-slate-700 text-slate-300"
}`}
>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-indigo-300 flex items-center gap-1.5">
<Sparkles className="h-3.5 w-3.5" /> Balanced Creative
</span>
{accuracyMode === "balanced" && <span className="h-2 w-2 rounded-full bg-indigo-400" />}
</div>
<p className="text-[11px] text-slate-400 leading-relaxed mb-2">
Balanced temperature (0.4) for natural conversational flow, brainstorming, and writing assistance.
</p>
<span className="text-[9.5px] font-mono font-bold text-indigo-400 uppercase">
{accuracyMode === "balanced" ? "Active Standard" : "Select Standard"}
</span>
</button>
<button
type="button"
onClick={() => {
if (onSelectAccuracyMode) onSelectAccuracyMode("fast");
if (onSelectUnderstandingMode) onSelectUnderstandingMode("grade-4");
}}
className={`p-4 rounded-xl border text-left transition cursor-pointer flex flex-col justify-between ${
accuracyMode === "fast"
? "bg-amber-950/40 border-amber-500/80 ring-2 ring-amber-500/30 text-white shadow-lg"
: "bg-slate-900/60 border-slate-800 hover:border-slate-700 text-slate-300"
}`}
>
<div className="flex items-center justify-between mb-2">
<span className="text-xs font-bold text-amber-300 flex items-center gap-1.5">
<Zap className="h-3.5 w-3.5" /> Ultra-Fast Flash
</span>
{accuracyMode === "fast" && <span className="h-2 w-2 rounded-full bg-amber-400" />}
</div>
<p className="text-[11px] text-slate-400 leading-relaxed mb-2">
High velocity sub-second responses optimized for rapid Q&A and instant summarization.
</p>
<span className="text-[9.5px] font-mono font-bold text-amber-400 uppercase">
{accuracyMode === "fast" ? "Active Standard" : "Select Standard"}
</span>
</button>
</div>
</div>
{/* Section 2: Added Google AI Features */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Cpu className="h-4 w-4 text-cyan-400" />
<h4 className="text-sm font-bold text-white">Google AI Connected Features & Tools</h4>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-cyan-500/10 text-cyan-300 border border-cyan-500/20 font-bold">
Gemini Grounding & Tools
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed">
Connect official Google intelligence engines into Aura Chat for real-time verification, geospatial intelligence, code execution, and flash streaming.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3.5">
{/* Google Maps Data & Grounding */}
<div className="p-4 rounded-xl bg-slate-900/90 border border-slate-800 flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
<div className="h-8 w-8 rounded-lg bg-emerald-500/10 text-emerald-400 flex items-center justify-center shrink-0 mt-0.5">
<MapPin className="h-4 w-4" />
</div>
<div>
<div className="text-xs font-bold text-white flex items-center gap-1.5">
<span>Google Maps Geospatial Data</span>
<span className="text-[9px] px-1.5 py-0.2 rounded bg-emerald-500/20 text-emerald-300 font-mono font-bold">LIVE</span>
</div>
<div className="text-[11px] text-slate-400 mt-0.5">
Grounds answers with Google Maps places, addresses, routing directions, traffic, and points of interest.
</div>
</div>
</div>
<button
type="button"
onClick={() => onToggleGoogleMapsGrounding && onToggleGoogleMapsGrounding(!googleMapsGrounding)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition shrink-0 cursor-pointer ${
googleMapsGrounding ? "bg-emerald-600 text-white shadow-sm" : "bg-slate-800 text-slate-400 hover:text-white"
}`}
>
{googleMapsGrounding ? "ON" : "OFF"}
</button>
</div>
{/* Google Search Real-Time Grounding */}
<div className="p-4 rounded-xl bg-slate-900/90 border border-slate-800 flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
<div className="h-8 w-8 rounded-lg bg-blue-500/10 text-blue-400 flex items-center justify-center shrink-0 mt-0.5">
<Globe className="h-4 w-4" />
</div>
<div>
<div className="text-xs font-bold text-white flex items-center gap-1.5">
<span>Google Search Live Grounding</span>
<span className="text-[9px] px-1.5 py-0.2 rounded bg-blue-500/20 text-blue-300 font-mono font-bold">2026 WEB</span>
</div>
<div className="text-[11px] text-slate-400 mt-0.5">
Fetches up-to-the-minute web information, current news, and verified factual citations.
</div>
</div>
</div>
<button
type="button"
onClick={() => onToggleGoogleSearchGrounding && onToggleGoogleSearchGrounding(!googleSearchGrounding)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition shrink-0 cursor-pointer ${
googleSearchGrounding ? "bg-blue-600 text-white shadow-sm" : "bg-slate-800 text-slate-400 hover:text-white"
}`}
>
{googleSearchGrounding ? "ON" : "OFF"}
</button>
</div>
{/* Google Fast-Flash Token Accelerator */}
<div className="p-4 rounded-xl bg-slate-900/90 border border-slate-800 flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
<div className="h-8 w-8 rounded-lg bg-amber-500/10 text-amber-400 flex items-center justify-center shrink-0 mt-0.5">
<Zap className="h-4 w-4" />
</div>
<div>
<div className="text-xs font-bold text-white flex items-center gap-1.5">
<span>Google Fast-Flash Accelerator</span>
<span className="text-[9px] px-1.5 py-0.2 rounded bg-amber-500/20 text-amber-300 font-mono font-bold">1-SEC BURST</span>
</div>
<div className="text-[11px] text-slate-400 mt-0.5">
Delivers instant streaming with zero artificial buffering for a flash of a second response speed.
</div>
</div>
</div>
<button
type="button"
onClick={() => onToggleFastFlashAccelerator && onToggleFastFlashAccelerator(!fastFlashAccelerator)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition shrink-0 cursor-pointer ${
fastFlashAccelerator ? "bg-amber-500 text-slate-950 font-black shadow-sm" : "bg-slate-800 text-slate-400 hover:text-white"
}`}
>
{fastFlashAccelerator ? "ON" : "OFF"}
</button>
</div>
{/* Google Code Sandbox & Python Execution */}
<div className="p-4 rounded-xl bg-slate-900/90 border border-slate-800 flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
<div className="h-8 w-8 rounded-lg bg-purple-500/10 text-purple-400 flex items-center justify-center shrink-0 mt-0.5">
<Terminal className="h-4 w-4" />
</div>
<div>
<div className="text-xs font-bold text-white flex items-center gap-1.5">
<span>Google Code Execution Sandbox</span>
<span className="text-[9px] px-1.5 py-0.2 rounded bg-purple-500/20 text-purple-300 font-mono font-bold">PYTHON</span>
</div>
<div className="text-[11px] text-slate-400 mt-0.5">
Executes live code in a sandboxed Python runtime for mathematical verification and computation.
</div>
</div>
</div>
<button
type="button"
onClick={() => onToggleGoogleCodeExecution && onToggleGoogleCodeExecution(!googleCodeExecution)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition shrink-0 cursor-pointer ${
googleCodeExecution ? "bg-purple-600 text-white shadow-sm" : "bg-slate-800 text-slate-400 hover:text-white"
}`}
>
{googleCodeExecution ? "ON" : "OFF"}
</button>
</div>
{/* Firebase / Aura Store Persistent Mode */}
<div className="p-4 rounded-xl bg-slate-900/90 border border-slate-800 flex items-start justify-between gap-3">
<div className="flex items-start gap-3">
<div className="h-8 w-8 rounded-lg bg-orange-500/10 text-orange-400 flex items-center justify-center shrink-0 mt-0.5">
<Database className="h-4 w-4" />
</div>
<div>
<div className="text-xs font-bold text-white flex items-center gap-1.5">
<span>Firebase Persistence Mode</span>
<span className="text-[9px] px-1.5 py-0.2 rounded bg-orange-500/20 text-orange-300 font-mono font-bold">STORE</span>
</div>
<div className="text-[11px] text-slate-400 mt-0.5">
Enables deep integration with Aura Store / Firebase for persistent cloud file management and historical session retrieval.
</div>
</div>
</div>
<button
type="button"
onClick={() => onToggleFirebaseEnabled && onToggleFirebaseEnabled(!firebaseEnabled)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition shrink-0 cursor-pointer ${
firebaseEnabled ? "bg-orange-600 text-white shadow-sm" : "bg-slate-800 text-slate-400 hover:text-white"
}`}
>
{firebaseEnabled ? "ON" : "OFF"}
</button>
</div>
</div>
</div>
{/* Section 2.5: Oxford English Academic Knowledge Base */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<BookOpen className="h-4 w-4 text-amber-400" />
<h4 className="text-sm font-bold text-white">Oxford Knowledge Base</h4>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-amber-500/10 text-amber-300 border border-amber-500/20 font-bold">
{isOxfordContextActive ? "100% LEXICAL ACTIVE" : "OFF"}
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed">
Grounds AI reasoning with academic English precision, verified lexical definitions, grammar mastery, and the complete Oxford English syllabus textbooks for Grades 6, 7, and 8.
</p>
<div className="p-4 rounded-xl bg-slate-900 border border-slate-800 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div className="flex items-center gap-3">
<div className="h-8 w-8 rounded-lg bg-amber-500/10 text-amber-400 flex items-center justify-center shrink-0">
<BookOpen className="h-4 w-4" />
</div>
<div>
<div className="text-xs font-bold text-white">Oxford Lexical & Syllabus Grounding</div>
<div className="text-[11px] text-slate-400">100% Academic & Lexical Precision across all conversations.</div>
</div>
</div>
<div className="flex items-center gap-2 shrink-0">
{onOpenOxfordModal && (
<button
type="button"
onClick={onOpenOxfordModal}
className="px-3 py-1.5 rounded-lg bg-slate-800 hover:bg-slate-700 text-amber-300 text-xs font-bold transition border border-slate-700 cursor-pointer"
>
Browse Oxford Library
</button>
)}
<button
type="button"
onClick={() => {
if (onToggleOxfordContext) onToggleOxfordContext(!isOxfordContextActive);
}}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition cursor-pointer ${
isOxfordContextActive ? "bg-amber-500 text-slate-950 font-black shadow-sm" : "bg-slate-800 text-slate-400 hover:text-white"
}`}
>
{isOxfordContextActive ? "ACTIVE" : "OFF"}
</button>
</div>
</div>
</div>
{/* Section 3: Automatic Theme (Light at 6 AM, dark at 6 PM) */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Sun className="h-4 w-4 text-indigo-400" />
<h4 className="text-sm font-bold text-white">Automatic Theme (Light at 6 AM, Dark at 6 PM)</h4>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-indigo-500/10 text-indigo-300 border border-indigo-500/20 font-bold">
{autoThemeEnabled ? "SCHEDULE ACTIVE" : "MANUAL MODE"}
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed">
Automatically switches the visual interface between sunlight daytime clarity and eye-safe twilight darkness according to real-world circadian time.
</p>
<div className="p-4 rounded-xl bg-slate-900 border border-slate-800 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div className="flex items-center gap-3">
<Clock className="h-5 w-5 text-indigo-400 shrink-0" />
<div>
<div className="text-xs font-bold text-white">Circadian Auto Theme Switcher</div>
<div className="text-[11px] text-slate-400">Light Mode at 6:00 AM ☀️ | Dark Mode at 6:00 PM 🌙</div>
</div>
</div>
<button
type="button"
disabled={!!(themeManualOverrideUntil && themeManualOverrideUntil > Date.now())}
onClick={() => {
if (onToggleAutoTheme) onToggleAutoTheme(!autoThemeEnabled);
}}
className={`px-4 py-2 rounded-xl text-xs font-bold transition shrink-0 cursor-pointer ${
themeManualOverrideUntil && themeManualOverrideUntil > Date.now()
? "bg-slate-800 text-slate-500 cursor-not-allowed"
: autoThemeEnabled
? "bg-indigo-600 text-white shadow-md"
: "bg-slate-800 text-slate-400 hover:text-white"
}`}
title={themeManualOverrideUntil && themeManualOverrideUntil > Date.now() ? "Automatic theme paused for 6 hours after manual toggle" : "Toggle automatic theme"}
>
{themeManualOverrideUntil && themeManualOverrideUntil > Date.now() ? "PAUSED (6h)" : autoThemeEnabled ? "ON" : "OFF"}
</button>
</div>
<div className="p-4 rounded-xl bg-slate-900 border border-slate-800 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div className="flex items-center gap-3">
<Sun className="h-5 w-5 text-amber-400 shrink-0" />
<div>
<div className="text-xs font-bold text-white">Current Mode: {darkMode ? "Dark Mode 🌙" : "Light Mode ☀️"}</div>
<div className="text-[11px] text-slate-400">Manual switch pauses circadian scheduling for 6 hours.</div>
</div>
</div>
<button
type="button"
onClick={() => {
if (onToggleDarkMode) onToggleDarkMode(!darkMode);
}}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-amber-300 text-xs font-bold transition border border-slate-700 shrink-0 cursor-pointer"
>
Switch to {darkMode ? "Light" : "Dark"} Mode
</button>
</div>
</div>
{/* Section 4: Aura Chat Theme */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Palette className="h-4 w-4 text-purple-400" />
<h4 className="text-sm font-bold text-white">Aura Chat Theme</h4>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-purple-500/10 text-purple-300 border border-purple-500/20 font-bold">
{AURA_THEMES.length} Themes Available
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed">
Choose a visual theme for Aura AI Chat. Dynamically colors the header, ambient glow, buttons, bubbles, and interactive surfaces.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
{AURA_THEMES.map((theme) => {
const isSelected = (auraThemeId || "aura-nebula") === theme.id;
return (
<button
key={theme.id}
type="button"
onClick={() => onSelectAuraTheme && onSelectAuraTheme(theme.id)}
className={`p-3.5 rounded-xl text-left border transition-all cursor-pointer flex flex-col justify-between group ${
isSelected
? "bg-slate-900 border-purple-500 ring-2 ring-purple-500/40 shadow-lg"
: "bg-slate-900/60 border-slate-800 hover:border-slate-700 hover:bg-slate-900"
}`}
>
<div className="flex items-center justify-between mb-2">
<span className={`font-bold text-xs bg-gradient-to-r ${theme.brandGradient} bg-clip-text text-transparent group-hover:${theme.brandHoverGradient}`}>
{theme.name}
</span>
{isSelected && (
<span className="h-4 w-4 rounded-full bg-emerald-500 text-white flex items-center justify-center shrink-0">
<Check className="h-2.5 w-2.5 stroke-[3]" />
</span>
)}
</div>
<div className="flex items-center justify-between gap-2 mt-2">
<div className="flex -space-x-1">
{theme.previewColors.map((col, idx) => (
<span
key={idx}
className="h-3.5 w-3.5 rounded-full border border-slate-900 shadow-xs"
style={{ backgroundColor: col }}
/>
))}
</div>
<span className="text-[9.5px] font-mono text-slate-400 group-hover:text-purple-300">
{isSelected ? "Active" : "Apply"}
</span>
</div>
</button>
);
})}
</div>
</div>
{/* Section 5: AI Voice Engine */}
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Volume2 className="h-4 w-4 text-cyan-400" />
<h4 className="text-sm font-bold text-white">AI Voice Engine</h4>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-cyan-500/10 text-cyan-300 border border-cyan-500/20 font-bold">
Auto-Detects Language & Best Voice
</span>
</div>
<p className="text-xs text-slate-400 leading-relaxed">
Configure voice synthesis for Live Voice Mode and spoken answers. Automatically detects user language and pairs the best natural neural voice.
</p>
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
<select
value={selectedVoiceURI || ""}
onChange={(e) => {
const val = e.target.value;
if (onSelectVoice) onSelectVoice(val);
}}
className="flex-1 px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-700 text-xs text-white font-medium focus:outline-none focus:ring-2 focus:ring-indigo-500 cursor-pointer"
>
<option value="">✨ Auto-Detect Language & Best Voice (Default)</option>
{voices.map((v) => (
<option key={v.voiceURI || v.name} value={v.voiceURI || v.name}>
{v.name} ({v.lang}) {v.default ? " — System Default" : ""}
</option>
))}
</select>
<button
type="button"
onClick={() => handleTestVoice(selectedVoiceURI)}
className="px-4 py-2.5 rounded-xl bg-cyan-500/10 hover:bg-cyan-500/20 border border-cyan-500/30 text-cyan-300 text-xs font-bold flex items-center justify-center gap-2 cursor-pointer transition active:scale-95 shrink-0"
>
<Volume2 className="h-4 w-4" />
<span>Test Voice 🔊</span>
</button>
</div>
</div>
{/* Section 6: Change Account Password & Security */}
{currentUser && (
<div className="bg-slate-950 p-5 rounded-2xl border border-slate-800/80 shadow-lg space-y-4">
<div className="flex items-center gap-2">
<Key className="h-4 w-4 text-amber-400" />
<h4 className="text-sm font-bold text-white">Change Account Password</h4>
</div>
<p className="text-xs text-slate-400">
Update security credentials for user @{currentUser.username}. Passwords must be at least 6 characters.
</p>
<form onSubmit={handleChangePasswordSubmit} className="space-y-3.5 max-w-md">
<div>
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-wider block mb-1">
Current Password
</label>
<input
type="password"
value={currentPasswordInput}
onChange={(e) => setCurrentPasswordInput(e.target.value)}
placeholder="Enter current password"
className="w-full px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-700 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-500"
required
/>
</div>
<div>
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-wider block mb-1">
New Password
</label>
<input
type="password"
value={newPasswordInput}
onChange={(e) => setNewPasswordInput(e.target.value)}
placeholder="Enter new password (min. 6 characters)"
className="w-full px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-700 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-500"
required
/>
</div>
<div>
<label className="text-[11px] font-bold text-slate-400 uppercase tracking-wider block mb-1">
Confirm New Password
</label>
<input
type="password"
value={confirmPasswordInput}
onChange={(e) => setConfirmPasswordInput(e.target.value)}
placeholder="Confirm new password"
className="w-full px-3.5 py-2.5 rounded-xl bg-slate-900 border border-slate-700 text-xs text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-amber-500"
required
/>
</div>
{passwordStatusMsg && (
<div className={`p-3 rounded-xl text-xs flex items-center gap-2 font-medium ${
passwordStatusMsg.success ? "bg-emerald-500/10 text-emerald-300 border border-emerald-500/20" : "bg-red-500/10 text-red-300 border border-red-500/20"
}`}>
{passwordStatusMsg.success ? <CheckCircle2 className="h-4 w-4 shrink-0" /> : <AlertTriangle className="h-4 w-4 shrink-0" />}
<span>{passwordStatusMsg.text}</span>
</div>
)}
<button
type="submit"
disabled={isSubmittingPassword}
className="px-5 py-2.5 rounded-xl bg-amber-500 hover:bg-amber-600 text-slate-950 font-bold text-xs flex items-center justify-center gap-2 cursor-pointer transition disabled:opacity-50 shadow-md"
>
{isSubmittingPassword ? <RefreshCw className="h-4 w-4 animate-spin" /> : <ShieldCheck className="h-4 w-4" />}
<span>Update Account Password</span>
</button>
</form>
</div>
)}
</div>
)}
{/* PAYMENT & UPGRADE MODAL */}
{upgradeModalTier && (
<div
className="absolute inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4"
onClick={() => setUpgradeModalTier(null)}
>
<div
className="bg-slate-900 border border-slate-700 rounded-3xl max-w-md w-full p-6 shadow-2xl space-y-4 text-white"
onClick={e => e.stopPropagation()}
>
<div className="flex items-center justify-between border-b border-slate-800 pb-3">
<div className="flex items-center gap-2">
<Award className="h-5 w-5 text-emerald-400" />
<h4 className="text-base font-bold">Upgrade to {TIER_CONFIGS[upgradeModalTier].name}</h4>
</div>
<button
onClick={() => setUpgradeModalTier(null)}
className="h-7 w-7 rounded-full bg-slate-800 hover:bg-slate-700 text-slate-300 flex items-center justify-center cursor-pointer"
>
<X className="h-4 w-4" />
</button>
</div>
{paymentSuccessMsg ? (
<div className="py-6 text-center space-y-2">
<CheckCircle2 className="h-10 w-10 text-emerald-400 mx-auto" />
<p className="text-sm font-bold text-emerald-300">{paymentSuccessMsg}</p>
</div>
) : (
<div className="space-y-4">
<div className="flex gap-2 p-1 bg-slate-950 rounded-xl border border-slate-800">
<button
onClick={() => setPaymentMethodTab("gpay")}
className={`flex-1 py-1.5 rounded-lg text-xs font-bold transition cursor-pointer ${
paymentMethodTab === "gpay" ? "bg-emerald-600 text-white" : "text-slate-400 hover:text-white"
}`}
>
Google Pay (9500076449)
</button>
<button
onClick={() => setPaymentMethodTab("razorpay")}
className={`flex-1 py-1.5 rounded-lg text-xs font-bold transition cursor-pointer ${
paymentMethodTab === "razorpay" ? "bg-indigo-600 text-white" : "text-slate-400 hover:text-white"
}`}
>
Razorpay Online
</button>
</div>
{paymentMethodTab === "gpay" ? (
<div className="space-y-3 text-xs">
<div className="p-3 bg-slate-950 rounded-xl border border-slate-800 space-y-2">
<div className="flex items-center justify-between">
<span className="text-slate-400">Google Pay Number:</span>
<div className="flex items-center gap-1.5">
<strong className="font-mono text-emerald-400 text-sm">9500076449</strong>
<button
onClick={handleCopyGpayNumber}
className="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-slate-200 text-[10px] font-bold cursor-pointer"
>
{copiedGpay ? "Copied!" : "Copy"}
</button>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-slate-400">UPI ID:</span>
<div className="flex items-center gap-1.5">
<strong className="font-mono text-emerald-400 text-sm">9500076449@okaxis</strong>
<button
onClick={handleCopyUpiId}
className="px-2 py-0.5 rounded bg-slate-800 hover:bg-slate-700 text-slate-200 text-[10px] font-bold cursor-pointer"
>
{copiedUpiId ? "Copied!" : "Copy"}
</button>
</div>
</div>
<div className="flex items-center justify-between pt-1 border-t border-slate-800 text-slate-300 font-bold">
<span>Amount to Pay:</span>
<span className="text-white text-sm">₹{TIER_CONFIGS[upgradeModalTier].priceINR}</span>
</div>
</div>
<div>
<label className="block text-slate-400 font-semibold mb-1">
Enter 12-digit UPI UTR / Transaction ID:
</label>
<input
type="text"
placeholder="e.g. 423819283748"
value={transactionIdInput}
onChange={e => setTransactionIdInput(e.target.value)}
className="w-full px-3 py-2 rounded-xl bg-slate-950 border border-slate-800 text-white font-mono text-xs focus:outline-none focus:border-emerald-500"
/>
<p className="text-[10px] text-slate-400 mt-1">
After paying on Google Pay to 9500076449, enter your UTR number above. Admin (Harsha) will verify and activate your tier!
</p>
</div>
<div className="flex justify-end gap-2 pt-2">
<button
type="button"
onClick={() => setUpgradeModalTier(null)}
className="px-3 py-2 rounded-xl bg-slate-800 text-slate-300 font-bold text-xs"
>
Cancel
</button>
<button
type="button"
onClick={handleSubmitUpiTransaction}
disabled={isSubmittingTxn || !transactionIdInput.trim()}
className="px-4 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-500 disabled:opacity-50 text-white font-bold text-xs flex items-center gap-1.5 cursor-pointer shadow-md"
>
{isSubmittingTxn ? <RefreshCw className="h-3.5 w-3.5 animate-spin" /> : <Send className="h-3.5 w-3.5" />}
<span>Submit for Admin Approval</span>
</button>
</div>
</div>
) : (
<div className="space-y-4 text-center py-4">
<CreditCard className="h-10 w-10 text-indigo-400 mx-auto" />
<div className="text-xs text-slate-300">
Instant automated payment checkout via Razorpay for <strong>₹{TIER_CONFIGS[upgradeModalTier].priceINR}</strong>.
</div>
<button
onClick={() => handleRazorpayPayment(upgradeModalTier)}
className="w-full py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs shadow-md cursor-pointer"
>
Pay ₹{TIER_CONFIGS[upgradeModalTier].priceINR} with Razorpay
</button>
</div>
)}
</div>
)}
</div>
</div>
)}
{/* Custom Confirmation Modal for Hugging Face Pull / Push Safety */}
{confirmHfModal && (
<div
className="absolute inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4 animate-fade-in"
onClick={() => setConfirmHfModal(null)}
>
<div
className="bg-slate-900 p-6 rounded-3xl max-w-md w-full flex flex-col border border-slate-700 shadow-2xl"
onClick={e => e.stopPropagation()}
>
<div className="flex items-center gap-3 text-amber-400 mb-3">
<div className="h-10 w-10 rounded-2xl bg-amber-500/20 border border-amber-500/30 flex items-center justify-center shrink-0">
<Database className="h-5 w-5" />
</div>
<div>
<h4 className="text-base font-bold text-white">
{confirmHfModal.action === "pull" ? "Pull from Hugging Face Bucket" : "Backup to Hugging Face Bucket"}
</h4>
<p className="text-xs text-slate-400 font-mono">{hfStatus?.repo || customHfRepo}</p>
</div>
</div>
<div className="my-3 p-3.5 rounded-2xl bg-slate-950 border border-slate-800 text-xs space-y-2 text-slate-300">
{confirmHfModal.action === "pull" ? (
<>
<p className="font-semibold text-amber-300 flex items-center gap-1.5">
<AlertTriangle className="h-4 w-4 shrink-0 text-amber-400" />
Overwrite Warning:
</p>
<p className="text-slate-300 leading-relaxed text-[11px]">
Pulling from Hugging Face will download the latest cloud snapshot and overwrite local storage files.
</p>
</>
) : (
<>
<p className="font-semibold text-emerald-300 flex items-center gap-1.5">
<UploadCloud className="h-4 w-4 shrink-0 text-emerald-400" />
Cloud Backup Confirmation:
</p>
<p className="text-slate-300 leading-relaxed text-[11px]">
This will upload all current Aura Store account databases, chats, and media files to your Hugging Face Dataset repository: <strong className="text-amber-300 font-mono">{hfStatus?.repo || customHfRepo}</strong>.
</p>
</>
)}
</div>
<div className="flex items-center justify-end gap-2.5 pt-3 border-t border-slate-800">
<button
type="button"
onClick={() => setConfirmHfModal(null)}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 font-bold text-xs cursor-pointer"
>
Cancel
</button>
<button
type="button"
onClick={confirmHfModal.action === "pull" ? handleHfPull : handleHfPush}
className={`px-4 py-2 rounded-xl text-white font-bold text-xs cursor-pointer shadow-md flex items-center gap-1.5 ${
confirmHfModal.action === "pull" ? "bg-amber-600 hover:bg-amber-500" : "bg-emerald-600 hover:bg-emerald-500"
}`}
>
{confirmHfModal.action === "pull" ? <Download className="h-3.5 w-3.5" /> : <UploadCloud className="h-3.5 w-3.5" />}
<span>{confirmHfModal.action === "pull" ? "Proceed with Pull" : "Proceed with Backup"}</span>
</button>
</div>
</div>
</div>
)}
{/* Delete Confirmation Modal */}
{confirmDeleteModal && (
<div
className="absolute inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4 animate-fade-in"
onClick={() => setConfirmDeleteModal(null)}
>
<div
className="bg-slate-900 p-6 rounded-3xl max-w-md w-full flex flex-col border border-slate-700 shadow-2xl"
onClick={e => e.stopPropagation()}
>
<div className="flex items-center gap-3 text-red-400 mb-3">
<div className="h-10 w-10 rounded-2xl bg-red-500/20 border border-red-500/30 flex items-center justify-center shrink-0">
<Trash2 className="h-5 w-5" />
</div>
<div>
<h4 className="text-base font-bold text-white">
Confirm Deletion ({confirmDeleteModal.itemsToDelete.length} {confirmDeleteModal.itemsToDelete.length === 1 ? "file" : "files"})
</h4>
<p className="text-xs text-slate-400">Reclaim persistent storage space.</p>
</div>
</div>
<p className="text-xs text-slate-300 leading-relaxed my-3">
Are you sure you want to permanently delete {confirmDeleteModal.itemsToDelete.length === 1 ? (
<strong className="text-white">{confirmDeleteModal.itemsToDelete[0].name}</strong>
) : (
<span>these {confirmDeleteModal.itemsToDelete.length} selected items</span>
)}? This will reclaim storage from local /dataai and the Hugging Face dataset bucket.
</p>
<div className="flex items-center justify-end gap-2.5 pt-3 border-t border-slate-800">
<button
type="button"
onClick={() => setConfirmDeleteModal(null)}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 font-bold text-xs cursor-pointer"
>
Cancel
</button>
<button
type="button"
onClick={executeDelete}
className="px-4 py-2 rounded-xl bg-red-600 hover:bg-red-500 text-white font-bold text-xs cursor-pointer shadow-md flex items-center gap-1.5"
>
<Trash2 className="h-3.5 w-3.5" />
<span>Permanently Delete</span>
</button>
</div>
</div>
</div>
)}
{/* Media Preview Modal */}
{previewItem && (
<div
className="absolute inset-0 z-50 bg-black/90 backdrop-blur-md flex items-center justify-center p-4"
onClick={() => setPreviewItem(null)}
>
<div
className="bg-slate-900 border border-slate-700 rounded-3xl max-w-2xl w-full p-4 shadow-2xl flex flex-col max-h-[85vh]"
onClick={e => e.stopPropagation()}
>
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<h4 className="text-xs font-bold text-white truncate max-w-md">{previewItem.name}</h4>
<button
onClick={() => setPreviewItem(null)}
className="h-7 w-7 rounded-full bg-slate-800 text-slate-300 flex items-center justify-center cursor-pointer"
>
<X className="h-4 w-4" />
</button>
</div>
<div className="flex-1 overflow-auto py-4 flex items-center justify-center">
{previewItem.name.match(/\.(jpeg|jpg|gif|png|webp|svg)$/i) ? (
<img src={previewItem.dataUrl} alt={previewItem.name} className="max-h-[60vh] max-w-full rounded-xl object-contain shadow-lg" />
) : (
<div className="p-4 bg-slate-950 rounded-xl font-mono text-xs text-slate-300 max-h-[50vh] overflow-auto whitespace-pre-wrap">
{previewItem.relativePath}
</div>
)}
</div>
</div>
</div>
)}
{/* Hugging Face Bucket & Token Configuration Modal */}
{showHfTokenModal && (
<div
className="absolute inset-0 z-50 bg-black/85 backdrop-blur-md flex items-center justify-center p-4 animate-fade-in"
onClick={() => setShowHfTokenModal(false)}
>
<div
className="bg-slate-900 border border-slate-700 rounded-3xl max-w-lg w-full p-6 shadow-2xl space-y-4 text-white"
onClick={e => e.stopPropagation()}
>
<div className="flex items-center justify-between pb-3 border-b border-slate-800">
<div className="flex items-center gap-2.5">
<div className="h-9 w-9 rounded-xl bg-amber-500/10 border border-amber-500/30 flex items-center justify-center text-amber-400">
<Database className="h-5 w-5" />
</div>
<div>
<h4 className="text-base font-bold">HF Bucket & /dataai Storage</h4>
<p className="text-xs text-slate-400">Configure your Hugging Face Bucket persistence</p>
</div>
</div>
<button
onClick={() => setShowHfTokenModal(false)}
className="h-8 w-8 rounded-full bg-slate-800 hover:bg-slate-700 text-slate-300 flex items-center justify-center cursor-pointer transition"
>
<X className="h-4 w-4" />
</button>
</div>
<div className="space-y-3.5 text-xs">
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1.5">
Hugging Face Dataset / Bucket ID
</label>
<input
type="text"
value={customHfRepo}
onChange={e => setCustomHfRepo(e.target.value)}
placeholder="e.g. harshasri28/harshasri28-Aura-Ai"
className="w-full px-3.5 py-2.5 rounded-xl bg-slate-950 border border-slate-800 text-white font-mono text-xs focus:outline-none focus:border-amber-500"
/>
<p className="text-[11px] text-slate-400 mt-1">
Files from <code className="text-amber-300 font-mono">/dataai</code> will automatically synchronize under the <code className="text-emerald-400 font-mono">dataai/</code> folder in this bucket.
</p>
</div>
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1.5">
Hugging Face Access Token (Write / Fine-Grained)
</label>
<input
type="password"
value={customHfToken}
onChange={e => setCustomHfToken(e.target.value)}
placeholder="hf_..."
className="w-full px-3.5 py-2.5 rounded-xl bg-slate-950 border border-slate-800 text-white font-mono text-xs focus:outline-none focus:border-amber-500"
/>
<p className="text-[11px] text-slate-400 mt-1">
Requires <span className="text-amber-300">write</span> permission to push files from /dataai to your dataset repository.
</p>
</div>
{/* Connection Test Result */}
{hfTestResult && (
<div className={`p-3.5 rounded-xl border text-xs space-y-1.5 ${
hfTestResult.success
? "bg-emerald-500/10 border-emerald-500/30 text-emerald-200"
: "bg-red-500/10 border-red-500/30 text-red-200"
}`}>
<div className="flex items-center gap-2 font-bold">
{hfTestResult.success ? (
<CheckCircle2 className="h-4 w-4 text-emerald-400 shrink-0" />
) : (
<AlertTriangle className="h-4 w-4 text-red-400 shrink-0" />
)}
<span>{hfTestResult.message}</span>
</div>
{hfTestResult.storagePath && (
<div className="text-[11px] text-slate-300 space-y-0.5 pt-1 border-t border-slate-800/80 font-mono">
<div>Local Root: <strong className="text-amber-300">{hfTestResult.storagePath}</strong></div>
<div>HF Bucket Path: <strong className="text-emerald-300">{hfTestResult.hfSubfolder}</strong></div>
{hfTestResult.repoType && <div>Repo Type: <span className="capitalize">{hfTestResult.repoType}</span></div>}
{hfTestResult.authenticatedUser && <div>HF User: <strong>{hfTestResult.authenticatedUser}</strong></div>}
</div>
)}
</div>
)}
{/* Test Connection & Action Buttons */}
<div className="pt-1 flex items-center justify-between gap-2">
<button
type="button"
onClick={handleTestHfConnection}
disabled={hfTestLoading}
className="px-3.5 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-amber-300 font-semibold text-xs flex items-center gap-1.5 cursor-pointer transition border border-slate-700 disabled:opacity-50"
>
<RefreshCw className={`h-3.5 w-3.5 ${hfTestLoading ? "animate-spin" : ""}`} />
<span>{hfTestLoading ? "Testing..." : "Test Connection & Access"}</span>
</button>
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => setShowHfTokenModal(false)}
className="px-4 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-300 font-semibold text-xs cursor-pointer transition"
>
Cancel
</button>
<button
type="button"
onClick={handleSaveHfConfig}
className="px-4 py-2 rounded-xl bg-amber-500 hover:bg-amber-400 text-slate-950 font-bold text-xs cursor-pointer transition shadow-md"
>
Save Configuration
</button>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</div>
);
}