From e558f4256ef79131e5e224e4d310c37e11c20137 Mon Sep 17 00:00:00 2001 From: Youssef Boubli Date: Tue, 9 Dec 2025 10:30:04 +0000 Subject: [PATCH 01/96] Initial commit --- .gitignore | 59 +- README.md | 82 +- app/(admin)/admin/cancellations/page.tsx | 190 + app/(admin)/admin/chat/page.tsx | 175 + app/(admin)/admin/layout.tsx | 135 + app/(admin)/admin/page.tsx | 119 + app/(admin)/admin/payments/page.tsx | 330 + app/(admin)/admin/settings/page.tsx | 330 + app/(admin)/admin/support/page.tsx | 33 + app/(admin)/admin/themes/page.tsx | 540 ++ app/(admin)/admin/tiers/page.tsx | 338 + app/(admin)/admin/users/page.tsx | 438 ++ app/(admin)/layout.tsx | 40 + app/(user)/layout.tsx | 29 + app/actions.ts | 231 + app/ai-chat/page.tsx | 492 ++ app/api/admin/action/route.ts | 93 + app/api/admin/users/route.ts | 63 + app/cancel-membership/page.tsx | 143 + app/compare/page.tsx | 130 + app/favorites/page.tsx | 132 + app/global-search/layout.tsx | 31 + app/global-search/page.tsx | 301 + app/globals.css | 18 + app/history/layout.tsx | 31 + app/history/page.tsx | 249 + app/layout.tsx | 54 +- app/login/page.tsx | 275 + app/maintenance/page.tsx | 129 + app/meal-planner/page.tsx | 116 + app/not-found.tsx | 73 + app/page.tsx | 416 +- app/product/[id]/page.tsx | 390 + app/profile/layout.tsx | 31 + app/profile/page.tsx | 334 + app/quest/page.tsx | 264 + app/recommendations/page.tsx | 357 + app/reset-password/page.tsx | 116 + app/scan/layout.tsx | 31 + app/scan/page.tsx | 209 + app/set-admin/page.tsx | 148 + app/signup/page.tsx | 184 + app/support/page.tsx | 56 + app/template.tsx | 20 + app/upgrade/page.tsx | 417 ++ build_error.txt | 30 + build_error_box.txt | 43 + build_final.txt | 65 + build_log.txt | 368 + build_log_2.txt | 36 + build_log_3.txt | 49 + build_log_fix.txt | 25 + components/admin/AdminChatWindow.tsx | 204 + components/admin/AdminSidebar.tsx | 281 + components/admin/StatsCard.tsx | 84 + components/admin/SystemControl.tsx | 86 + components/admin/UpgradeRequestList.tsx | 156 + components/admin/UserTable.tsx | 96 + components/ads/AdBanner.tsx | 61 + components/ads/InterstitialAd.tsx | 126 + components/animation/ExamplePage.tsx | 57 + components/animation/PageTransition.tsx | 50 + components/animation/ScrollReveal.tsx | 44 + components/animation/StaggerList.tsx | 71 + components/chat/ChatErrorCard.tsx | 255 + components/compare/AddToCompareButton.tsx | 79 + components/compare/CompareFloatingBar.tsx | 101 + components/features/ARProductVisualizer.tsx | 182 + .../features/AiPreferenceOnboarding.tsx | 378 + components/features/GradeDisplay.tsx | 101 + components/features/ProAnalysisView.tsx | 288 + components/features/ProductCard.tsx | 173 + components/features/RecommendationCard.tsx | 129 + .../features/RecommendationPairCard.tsx | 253 + components/features/support/ChatWindow.tsx | 102 + components/layout/ChatButton.tsx | 27 + components/layout/Footer.tsx | 66 + components/layout/MobileBottomNav.tsx | 119 + components/layout/Navbar.tsx | 171 + components/product/AITruthDetector.tsx | 134 + components/settings/AIChatSettings.tsx | 196 + components/settings/ApiKeyManager.tsx | 420 ++ components/subscription/FeatureList.tsx | 52 + components/subscription/TierBadge.tsx | 71 + components/subscription/UpgradePrompt.tsx | 119 + components/theme/ThemeCustomizer.tsx | 342 + components/theme/ThemeProvider.tsx | 47 + components/theme/ThemeSelector.tsx | 356 + components/theme/ThemeToggle.tsx | 125 + components/ui/AnimatedButton.tsx | 36 + components/ui/AnimatedCard.tsx | 56 + components/ui/EmptyState.tsx | 106 + components/ui/GlassCard.tsx | 35 + components/ui/LoadingSpinner.tsx | 200 + components/ui/SearchingAnimation.tsx | 94 + components/ui/SkeletonCard.tsx | 78 + components/ui/WelcomeModal.tsx | 186 + components/user/DietaryPreferencesSection.tsx | 312 + components/user/UserHome.tsx | 451 ++ context/AuthContext.tsx | 253 + context/CompareContext.tsx | 62 + dev_crash_log_2.txt | 20 + dev_debug_log.txt | 20 + dev_error_log.txt | 20 + env.example | 7 + firestore.rules | 131 + lib/animations/variants.ts | 90 + lib/firebase.ts | 32 + lib/firebaseAdmin.ts | 30 + lib/hooks/useTheme.ts | 12 + lib/theme/ThemeContext.tsx | 353 + lib/theme/muiTheme.ts | 239 + lib/theme/themes.ts | 324 + next-env.d.ts | 6 + next.config.ts | 26 +- package-lock.json | 6418 +++++++++++++++-- package.json | 19 +- public/manifest.json | 29 + scripts/createProUser.js | 65 + scripts/createProUser.ts | 40 + scripts/setAdminRole.ts | 45 + scripts/testGrading.ts | 82 + services/adminService.ts | 108 + services/aiChatService.ts | 364 + services/aiService.ts | 747 ++ services/external/foodbService.ts | 56 + services/external/usdaService.ts | 66 + services/externalProductService.ts | 94 + services/favoriteService.ts | 44 + services/gradingService.ts | 70 + services/historyService.ts | 315 + services/mealPlannerService.ts | 55 + services/paymentService.ts | 317 + services/productService.ts | 209 + services/questService.ts | 50 + services/recommendationService.ts | 283 + services/scanService.ts | 57 + services/smartGradingService.ts | 138 + services/statsService.ts | 129 + services/subscriptionService.ts | 199 + services/supportService.ts | 188 + services/sustainabilityService.ts | 37 + services/systemService.ts | 107 + services/themeService.ts | 409 ++ services/userService.ts | 37 + theme/ThemeRegistry.tsx | 17 + theme/theme.ts | 104 + types/aiChat.ts | 59 + types/chat.ts | 27 + types/payment.ts | 27 + types/quest.ts | 18 + types/react-qr-scanner.d.ts | 15 + types/system.ts | 39 + types/theme.ts | 114 + types/user.ts | 152 + utils/setAdmin.ts | 36 + y/index.html | 89 + 157 files changed, 28676 insertions(+), 567 deletions(-) create mode 100644 app/(admin)/admin/cancellations/page.tsx create mode 100644 app/(admin)/admin/chat/page.tsx create mode 100644 app/(admin)/admin/layout.tsx create mode 100644 app/(admin)/admin/page.tsx create mode 100644 app/(admin)/admin/payments/page.tsx create mode 100644 app/(admin)/admin/settings/page.tsx create mode 100644 app/(admin)/admin/support/page.tsx create mode 100644 app/(admin)/admin/themes/page.tsx create mode 100644 app/(admin)/admin/tiers/page.tsx create mode 100644 app/(admin)/admin/users/page.tsx create mode 100644 app/(admin)/layout.tsx create mode 100644 app/(user)/layout.tsx create mode 100644 app/actions.ts create mode 100644 app/ai-chat/page.tsx create mode 100644 app/api/admin/action/route.ts create mode 100644 app/api/admin/users/route.ts create mode 100644 app/cancel-membership/page.tsx create mode 100644 app/compare/page.tsx create mode 100644 app/favorites/page.tsx create mode 100644 app/global-search/layout.tsx create mode 100644 app/global-search/page.tsx create mode 100644 app/history/layout.tsx create mode 100644 app/history/page.tsx create mode 100644 app/login/page.tsx create mode 100644 app/maintenance/page.tsx create mode 100644 app/meal-planner/page.tsx create mode 100644 app/not-found.tsx create mode 100644 app/product/[id]/page.tsx create mode 100644 app/profile/layout.tsx create mode 100644 app/profile/page.tsx create mode 100644 app/quest/page.tsx create mode 100644 app/recommendations/page.tsx create mode 100644 app/reset-password/page.tsx create mode 100644 app/scan/layout.tsx create mode 100644 app/scan/page.tsx create mode 100644 app/set-admin/page.tsx create mode 100644 app/signup/page.tsx create mode 100644 app/support/page.tsx create mode 100644 app/template.tsx create mode 100644 app/upgrade/page.tsx create mode 100644 build_error.txt create mode 100644 build_error_box.txt create mode 100644 build_final.txt create mode 100644 build_log.txt create mode 100644 build_log_2.txt create mode 100644 build_log_3.txt create mode 100644 build_log_fix.txt create mode 100644 components/admin/AdminChatWindow.tsx create mode 100644 components/admin/AdminSidebar.tsx create mode 100644 components/admin/StatsCard.tsx create mode 100644 components/admin/SystemControl.tsx create mode 100644 components/admin/UpgradeRequestList.tsx create mode 100644 components/admin/UserTable.tsx create mode 100644 components/ads/AdBanner.tsx create mode 100644 components/ads/InterstitialAd.tsx create mode 100644 components/animation/ExamplePage.tsx create mode 100644 components/animation/PageTransition.tsx create mode 100644 components/animation/ScrollReveal.tsx create mode 100644 components/animation/StaggerList.tsx create mode 100644 components/chat/ChatErrorCard.tsx create mode 100644 components/compare/AddToCompareButton.tsx create mode 100644 components/compare/CompareFloatingBar.tsx create mode 100644 components/features/ARProductVisualizer.tsx create mode 100644 components/features/AiPreferenceOnboarding.tsx create mode 100644 components/features/GradeDisplay.tsx create mode 100644 components/features/ProAnalysisView.tsx create mode 100644 components/features/ProductCard.tsx create mode 100644 components/features/RecommendationCard.tsx create mode 100644 components/features/RecommendationPairCard.tsx create mode 100644 components/features/support/ChatWindow.tsx create mode 100644 components/layout/ChatButton.tsx create mode 100644 components/layout/Footer.tsx create mode 100644 components/layout/MobileBottomNav.tsx create mode 100644 components/layout/Navbar.tsx create mode 100644 components/product/AITruthDetector.tsx create mode 100644 components/settings/AIChatSettings.tsx create mode 100644 components/settings/ApiKeyManager.tsx create mode 100644 components/subscription/FeatureList.tsx create mode 100644 components/subscription/TierBadge.tsx create mode 100644 components/subscription/UpgradePrompt.tsx create mode 100644 components/theme/ThemeCustomizer.tsx create mode 100644 components/theme/ThemeProvider.tsx create mode 100644 components/theme/ThemeSelector.tsx create mode 100644 components/theme/ThemeToggle.tsx create mode 100644 components/ui/AnimatedButton.tsx create mode 100644 components/ui/AnimatedCard.tsx create mode 100644 components/ui/EmptyState.tsx create mode 100644 components/ui/GlassCard.tsx create mode 100644 components/ui/LoadingSpinner.tsx create mode 100644 components/ui/SearchingAnimation.tsx create mode 100644 components/ui/SkeletonCard.tsx create mode 100644 components/ui/WelcomeModal.tsx create mode 100644 components/user/DietaryPreferencesSection.tsx create mode 100644 components/user/UserHome.tsx create mode 100644 context/AuthContext.tsx create mode 100644 context/CompareContext.tsx create mode 100644 dev_crash_log_2.txt create mode 100644 dev_debug_log.txt create mode 100644 dev_error_log.txt create mode 100644 env.example create mode 100644 firestore.rules create mode 100644 lib/animations/variants.ts create mode 100644 lib/firebase.ts create mode 100644 lib/firebaseAdmin.ts create mode 100644 lib/hooks/useTheme.ts create mode 100644 lib/theme/ThemeContext.tsx create mode 100644 lib/theme/muiTheme.ts create mode 100644 lib/theme/themes.ts create mode 100644 next-env.d.ts create mode 100644 public/manifest.json create mode 100644 scripts/createProUser.js create mode 100644 scripts/createProUser.ts create mode 100644 scripts/setAdminRole.ts create mode 100644 scripts/testGrading.ts create mode 100644 services/adminService.ts create mode 100644 services/aiChatService.ts create mode 100644 services/aiService.ts create mode 100644 services/external/foodbService.ts create mode 100644 services/external/usdaService.ts create mode 100644 services/externalProductService.ts create mode 100644 services/favoriteService.ts create mode 100644 services/gradingService.ts create mode 100644 services/historyService.ts create mode 100644 services/mealPlannerService.ts create mode 100644 services/paymentService.ts create mode 100644 services/productService.ts create mode 100644 services/questService.ts create mode 100644 services/recommendationService.ts create mode 100644 services/scanService.ts create mode 100644 services/smartGradingService.ts create mode 100644 services/statsService.ts create mode 100644 services/subscriptionService.ts create mode 100644 services/supportService.ts create mode 100644 services/sustainabilityService.ts create mode 100644 services/systemService.ts create mode 100644 services/themeService.ts create mode 100644 services/userService.ts create mode 100644 theme/ThemeRegistry.tsx create mode 100644 theme/theme.ts create mode 100644 types/aiChat.ts create mode 100644 types/chat.ts create mode 100644 types/payment.ts create mode 100644 types/quest.ts create mode 100644 types/react-qr-scanner.d.ts create mode 100644 types/system.ts create mode 100644 types/theme.ts create mode 100644 types/user.ts create mode 100644 utils/setAdmin.ts create mode 100644 y/index.html diff --git a/.gitignore b/.gitignore index 5ef6a52..c926d3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,48 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies +# Dependencies /node_modules /.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing +.pnp.js + +# Testing /coverage -# next.js +# Next.js /.next/ /out/ -# production +# Production /build -# misc +# Environment Variables +.env +.env.local +.env.production +.env.development +.env.test +.env*.local + +# Firebase +.firebase/ +firebase-debug.log +firebase-debug.log* + +# Vercel +.vercel/ + +# System & Editor .DS_Store +.vscode/ +.idea/ *.pem -# debug +# TypeScript +*.tsbuildinfo + +# Debug Logs npm-debug.log* yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* +pnpm-debug.log* -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +# Local Security/Keys (Preserved from previous configuration) +certificates/ +serviceAccountKey.json diff --git a/README.md b/README.md index e215bc4..7be6456 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,72 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# TruthLens 🔍🥗 -## Getting Started +**TruthLens** is a modern, AI-powered Progressive Web App (PWA) designed to demystify the food you eat. By combining advanced computer vision with powerful Large Language Models (LLMs), TruthLens helps users scan products, analyze ingredients, and make healthier dietary choices instantly. -First, run the development server: +## 🌟 Key Features -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +* **🥗 Smart Ingredient Analysis**: Scan product barcodes or labels to instantly get a breakdown of ingredients, revealing hidden additives, allergens, and nutritional value. +* **🤖 Empowered by AI**: + * **AI Chat Assistant**: Have a natural conversation with our AI about food, nutrition, and health goals (`/ai-chat`). + * **Multi-Provider Support**: Powered by **Groq** (Llama 3) and **Google Gemini** for fast, accurate insights. + * **Personalized Advice**: Recommendations tailored to your specific dietary needs (Vegan, Gluten-Free, Keto, etc.). +* **📊 Comprehensive Scoring**: Get an instant "Health Score" for every product, backed by scientific data. +* **💎 Tiered Membership System**: + * **Free/Plus**: Bring Your Own Key (BYOK) for AI features. + * **Pro/Ultimate**: Unlimited, hassle-free access with platform-managed API keys. +* **🎨 Beautiful, Adaptive UI**: + * Fully customizable themes with Dark/Light mode support. + * Smooth animations powered by Framer Motion. + * Responsive design that works perfectly on Mobile, Tablet, and Desktop. +* **💬 Dual Support Channels**: + * **AI Assistant**: For instant nutrition answers. + * **Support Chat**: Direct chat line to customer service for app help (`/support`). -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +## 🚀 Tech Stack -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +* **Frontend**: Next.js 16 (React 19), TypeScript, Tailwind CSS, Material UI v7. +* **Backend**: Firebase (Firestore, Auth, Storage). +* **AI Integration**: Groq SDK, Google Generative AI SDK, Framer Motion. +* **Platform**: PWA (Progressive Web App) with experimental HTTPS support. -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +## 🛠️ Getting Started -## Learn More +### Prerequisites -To learn more about Next.js, take a look at the following resources: +* Node.js 18+ installed on your machine. +* Firebase project configured (with `.env` or `firebaseConfig` setup). -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +### Installation -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! +1. **Clone the repository**: + ```bash + git clone https://github.com/yourusername/truth-lens.git + cd truth-lens + ``` -## Deploy on Vercel +2. **Install dependencies**: + ```bash + npm install + # or + yarn install + ``` -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. +3. **Run the development server**: + ```bash + npm run dev + ``` -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +4. **Open the app**: + Visit `https://localhost:3000` (Note: Uses HTTPS). + +## 📂 Project Structure + +* `/app`: Next.js App Router pages and layouts. + * `/ai-chat`: AI Assistant interface. + * `/support`: Human support chat interface. + * `/scan`: Barcode/Product scanning feature. +* `/components`: Reusable UI components. +* `/services`: Business logic and API integrations (Firebase, AI). +* `/lib`: Core configuration (Firebase, Theme). + +--- +*Powered by TruthLens Team* diff --git a/app/(admin)/admin/cancellations/page.tsx b/app/(admin)/admin/cancellations/page.tsx new file mode 100644 index 0000000..09525d8 --- /dev/null +++ b/app/(admin)/admin/cancellations/page.tsx @@ -0,0 +1,190 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import { + Box, Typography, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, + Chip, IconButton, Tooltip, CircularProgress, Button, Dialog, DialogTitle, DialogContent, DialogActions, DialogContentText +} from '@mui/material'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import CancelIcon from '@mui/icons-material/Cancel'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { getCancellationRequests, updateUserTier } from '@/services/subscriptionService'; +import { doc, updateDoc, deleteDoc } from 'firebase/firestore'; +import { db } from '@/lib/firebase'; + +interface CancellationRequest { + id: string; + userId: string; + email: string; + reason: string; + status: 'pending' | 'approved' | 'rejected' | 'completed'; + createdAt: any; +} + +export default function CancellationRequestsPage() { + const [requests, setRequests] = useState([]); + const [loading, setLoading] = useState(true); + const [actionLoading, setActionLoading] = useState(null); + + useEffect(() => { + fetchRequests(); + }, []); + + const fetchRequests = async () => { + setLoading(true); + const data = await getCancellationRequests(); + setRequests(data as CancellationRequest[]); + setLoading(false); + }; + + const handleApprove = async (request: CancellationRequest) => { + if (!confirm('Are you sure you want to approve this cancellation? This will downgrade the user to FREE tier immediately.')) return; + + setActionLoading(request.id); + try { + // 1. Update User Tier to Free + await updateUserTier(request.userId, 'free'); + + // 2. Update Request Status + const requestRef = doc(db, 'cancellationRequests', request.id); + await updateDoc(requestRef, { status: 'completed' }); + + // 3. Refresh List + await fetchRequests(); + } catch (error) { + console.error('Error approving cancellation:', error); + alert('Failed to approve cancellation.'); + } finally { + setActionLoading(null); + } + }; + + const handleReject = async (request: CancellationRequest) => { + if (!confirm('Are you sure you want to reject this cancellation?')) return; + + setActionLoading(request.id); + try { + const requestRef = doc(db, 'cancellationRequests', request.id); + await updateDoc(requestRef, { status: 'rejected' }); + await fetchRequests(); + } catch (error) { + console.error('Error rejecting cancellation:', error); + alert('Failed to reject cancellation.'); + } finally { + setActionLoading(null); + } + }; + + const handleDelete = async (id: string) => { + if (!confirm('Are you sure you want to delete this record?')) return; + + try { + await deleteDoc(doc(db, 'cancellationRequests', id)); + setRequests(prev => prev.filter(r => r.id !== id)); + } catch (error) { + console.error('Error deleting request:', error); + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case 'pending': return 'warning'; + case 'approved': + case 'completed': return 'success'; + case 'rejected': return 'error'; + default: return 'default'; + } + }; + + if (loading) { + return ( + + + + ); + } + + return ( + + + Membership Cancellations + + + + + + + + User Email + Reason + Date + Status + Actions + + + + {requests.length === 0 ? ( + + + No cancellation requests found. + + + ) : ( + requests.map((request) => ( + + {request.email} + + + {request.reason} + + + + {request.createdAt?.toDate ? request.createdAt.toDate().toLocaleDateString() : 'N/A'} + + + + + + {request.status === 'pending' && ( + + + handleReject(request)} + disabled={actionLoading === request.id} + > + + + + )} + {request.status !== 'pending' && ( + handleDelete(request.id)} color="error" size="small"> + + + )} + + + )) + )} + +
+
+
+
+ ); +} diff --git a/app/(admin)/admin/chat/page.tsx b/app/(admin)/admin/chat/page.tsx new file mode 100644 index 0000000..85aee3f --- /dev/null +++ b/app/(admin)/admin/chat/page.tsx @@ -0,0 +1,175 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import { listenForAdminChatList, clearAllChats } from '@/services/supportService'; +import AdminChatWindow from '@/components/admin/AdminChatWindow'; +import { useAuth } from '@/context/AuthContext'; +import { Chat } from '@/types/chat'; +import { Trash2, Search, Filter, MessageSquare } from 'lucide-react'; +import { AnimatePresence, motion } from 'framer-motion'; + +export default function AdminChatPage() { + const { user } = useAuth(); + const [chats, setChats] = useState([]); + const [selectedChatId, setSelectedChatId] = useState(null); + const [isClearing, setIsClearing] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + + useEffect(() => { + const unsubscribe = listenForAdminChatList((newChats) => { + setChats(newChats); + }); + return () => unsubscribe(); + }, []); + + const handleClearAll = async () => { + if (!confirm("Are you sure you want to DELETE ALL chats and messages? This cannot be undone.")) return; + + setIsClearing(true); + try { + await clearAllChats(); + setSelectedChatId(null); + } catch (error) { + console.error("Failed to clear chats:", error); + alert("Failed to clear chats."); + } finally { + setIsClearing(false); + } + }; + + const selectedChat = chats.find(c => c.id === selectedChatId); + + if (!user) return null; + + // View A: The Chat List + const ChatListView = ( +
+ {/* Header */} +
+
+

Messages

+ +
+ + {/* Search Bar */} +
+ + setSearchQuery(e.target.value)} + className="w-full bg-white/20 backdrop-blur-md text-white placeholder-blue-100 border border-white/30 rounded-xl py-3 pl-10 pr-4 focus:outline-none focus:bg-white/30 transition-all font-medium" + /> +
+
+ + {/* List */} +
+ {chats.filter(c => c.userEmail?.toLowerCase().includes(searchQuery.toLowerCase()) || c.id.includes(searchQuery)).length === 0 ? ( +
+

No conversations found

+
+ ) : ( + chats + .filter(c => c.userEmail?.toLowerCase().includes(searchQuery.toLowerCase()) || c.id.includes(searchQuery)) + .map(chat => ( + setSelectedChatId(chat.id)} + className={`w-full text-left p-4 rounded-2xl shadow-sm border transition-all flex items-center gap-4 group ${selectedChatId === chat.id + ? 'bg-white border-blue-400 shadow-blue-100' + : 'bg-white border-transparent hover:border-gray-200' + }`} + > +
+
+ {chat.userEmail?.[0]?.toUpperCase() || 'U'} +
+ {/* Online Status Indicator (mock) */} +
+
+ +
+
+ + {chat.userEmail || "Anonymous User"} + + + {chat.updatedAt?.toDate?.().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + +
+

+ {chat.lastMessage?.text || "Started a new conversation"} +

+
+
+ )) + )} +
+
+ ); + + return ( +
+ {/* Desktop: Sidebar (List) */} +
+ {ChatListView} +
+ + {/* Desktop & Mobile: Main Content (Conversation) */} +
+ + {selectedChatId ? ( + + setSelectedChatId(null)} + /> + + ) : ( +
+
+
+ +
+
+

Select a Conversation

+

+ Choose a chat from the sidebar to start messaging your users. +

+
+ )} +
+
+
+ ); +} + diff --git a/app/(admin)/admin/layout.tsx b/app/(admin)/admin/layout.tsx new file mode 100644 index 0000000..60ca7bc --- /dev/null +++ b/app/(admin)/admin/layout.tsx @@ -0,0 +1,135 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import { Box, AppBar, Toolbar, Typography, IconButton, useTheme, useMediaQuery, alpha } from '@mui/material'; +import { usePathname } from 'next/navigation'; +import { AnimatePresence } from 'framer-motion'; +import PageTransition from '@/components/animation/PageTransition'; +import AdminSidebar from '@/components/admin/AdminSidebar'; +import Footer from '@/components/layout/Footer'; +import MenuIcon from '@mui/icons-material/MenuRounded'; +import CloseIcon from '@mui/icons-material/CloseRounded'; +import NotificationsIcon from '@mui/icons-material/NotificationsNoneRounded'; + +import { Badge } from '@mui/material'; +import { listenForAdminUnreadCount } from '@/services/supportService'; + +const SIDEBAR_WIDTH = 280; + +export default function AdminLayout({ children }: { children: React.ReactNode }) { + const pathname = usePathname(); + const [mobileOpen, setMobileOpen] = useState(false); + const [unreadCount, setUnreadCount] = useState(0); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + + useEffect(() => { + const unsubscribe = listenForAdminUnreadCount((count) => { + setUnreadCount(count); + }); + return () => unsubscribe(); + }, []); + + const handleDrawerToggle = () => { + setMobileOpen(!mobileOpen); + }; + + const getPageTitle = (path: string) => { + const parts = path.split('/').filter(Boolean); + if (parts.length === 1 && parts[0] === 'admin') return 'Dashboard'; + const lastPart = parts[parts.length - 1]; + return lastPart.charAt(0).toUpperCase() + lastPart.slice(1); + }; + + return ( + + {/* Sidebar Navigation */} + + + {/* Main Content Area */} + + {/* Glassmorphic AppBar */} + + + {/* Hamburger Menu (Mobile Only) */} + + {mobileOpen ? : } + + + + {getPageTitle(pathname)} + + + {/* Right Side Actions */} + + + + + + + + + {/* Page Content Container */} + + + + {children} + + + + + {/* Footer */} + +