-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheslint-errors.json
More file actions
1 lines (1 loc) 路 743 KB
/
Copy patheslint-errors.json
File metadata and controls
1 lines (1 loc) 路 743 KB
1
锘縖{"filePath":"D:\\Visual_Code\\StreamVerse\\eslint.config.mjs","messages":[{"ruleId":"import/no-anonymous-default-export","severity":1,"message":"Assign array to a variable before exporting as module default","line":3,"column":1,"nodeType":"ExportDefaultDeclaration","endLine":3,"endColumn":26}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import next from \"eslint-config-next\";\r\n\r\nexport default [...next];","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\next.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\public\\sw.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\ai\\flows\\recommendations-flow.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\ai\\genkit.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\about\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":113,"column":183,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[4763,5045],"text":"\n StreamVerse sources all movie and TV show information from The Movie Database (TMDB), one of the most comprehensive and up-to-date entertainment databases available. We're grateful for their extensive API and the vast collection of content they maintain.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[4763,5045],"text":"\n StreamVerse sources all movie and TV show information from The Movie Database (TMDB), one of the most comprehensive and up-to-date entertainment databases available. We‘re grateful for their extensive API and the vast collection of content they maintain.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[4763,5045],"text":"\n StreamVerse sources all movie and TV show information from The Movie Database (TMDB), one of the most comprehensive and up-to-date entertainment databases available. We're grateful for their extensive API and the vast collection of content they maintain.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[4763,5045],"text":"\n StreamVerse sources all movie and TV show information from The Movie Database (TMDB), one of the most comprehensive and up-to-date entertainment databases available. We’re grateful for their extensive API and the vast collection of content they maintain.\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":128,"column":255,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5549,5829],"text":"\n We envision a future where streaming entertainment is accessible, personalized, and enjoyable for everyone. Our platform is designed to be intuitive and feature-rich, allowing users to easily discover new content and keep track of what they've watched.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[5549,5829],"text":"\n We envision a future where streaming entertainment is accessible, personalized, and enjoyable for everyone. Our platform is designed to be intuitive and feature-rich, allowing users to easily discover new content and keep track of what they‘ve watched.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5549,5829],"text":"\n We envision a future where streaming entertainment is accessible, personalized, and enjoyable for everyone. Our platform is designed to be intuitive and feature-rich, allowing users to easily discover new content and keep track of what they've watched.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[5549,5829],"text":"\n We envision a future where streaming entertainment is accessible, personalized, and enjoyable for everyone. Our platform is designed to be intuitive and feature-rich, allowing users to easily discover new content and keep track of what they’ve watched.\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":131,"column":53,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5849,6074],"text":"\n StreamVerse is constantly evolving. We're committed to adding new features, improving performance, and ensuring the best possible user experience. Your feedback helps us grow and improve every day.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[5849,6074],"text":"\n StreamVerse is constantly evolving. We‘re committed to adding new features, improving performance, and ensuring the best possible user experience. Your feedback helps us grow and improve every day.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5849,6074],"text":"\n StreamVerse is constantly evolving. We're committed to adding new features, improving performance, and ensuring the best possible user experience. Your feedback helps us grow and improve every day.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[5849,6074],"text":"\n StreamVerse is constantly evolving. We’re committed to adding new features, improving performance, and ensuring the best possible user experience. Your feedback helps us grow and improve every day.\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":189,"column":31,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[8454,8521],"text":"\n Have questions? We'd love to hear from you.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[8454,8521],"text":"\n Have questions? We‘d love to hear from you.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[8454,8521],"text":"\n Have questions? We'd love to hear from you.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[8454,8521],"text":"\n Have questions? We’d love to hear from you.\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Star, Users, Zap, Code2 } from 'lucide-react';\nimport Link from 'next/link';\n\nexport default function AboutUs() {\n const features = [\n {\n icon: Star,\n title: \"Quality Content\",\n description: \"Access thousands of movies and TV shows from The Movie Database\"\n },\n {\n icon: Users,\n title: \"Community Focus\",\n description: \"Build your watchlist, rate content, and track your viewing history\"\n },\n {\n icon: Zap,\n title: \"Lightning Fast\",\n description: \"Powered by Next.js and modern web technologies for optimal performance\"\n },\n {\n icon: Code2,\n title: \"Open Source\",\n description: \"Built with passion using cutting-edge technology and best practices\"\n }\n ];\n\n return (\n <div className=\"min-h-screen bg-gradient-to-b from-background to-background/80\">\n {/* Header */}\n <div className=\"bg-gradient-to-r from-primary/20 to-primary/5 py-12 border-b\">\n <div className=\"container mx-auto px-4\">\n <h1 className=\"text-4xl font-bold mb-4\">About StreamVerse</h1>\n <p className=\"text-lg text-muted-foreground\">\n Your ultimate streaming companion for movies and TV shows\n </p>\n </div>\n </div>\n\n {/* Hero Section */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"bg-card border border-border rounded-lg p-8 md:p-12 mb-12\">\n <h2 className=\"text-3xl font-bold mb-6 text-foreground\">Our Mission</h2>\n <p className=\"text-lg text-muted-foreground leading-relaxed\">\n StreamVerse is dedicated to providing a modern, user-friendly platform for discovering and enjoying movies and television shows. We believe everyone should have easy access to quality entertainment content with the ability to personalize their experience through ratings, watchlists, and viewing history.\n </p>\n </div>\n </div>\n </div>\n\n {/* Features */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n <h2 className=\"text-3xl font-bold mb-12 text-foreground text-center\">Why Choose StreamVerse?</h2>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-8\">\n {features.map((feature, idx) => {\n const IconComponent = feature.icon;\n return (\n <div key={idx} className=\"bg-card border border-border rounded-lg p-6 hover:border-primary/50 transition-colors\">\n <IconComponent className=\"h-8 w-8 text-primary mb-4\" />\n <h3 className=\"text-xl font-bold mb-2 text-foreground\">{feature.title}</h3>\n <p className=\"text-muted-foreground\">{feature.description}</p>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n\n {/* Technology Stack */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n <h2 className=\"text-3xl font-bold mb-8 text-foreground\">Built With Modern Technology</h2>\n <div className=\"bg-card border border-border rounded-lg p-8\">\n <div className=\"grid grid-cols-2 md:grid-cols-3 gap-6\">\n <div>\n <h4 className=\"font-semibold text-foreground mb-2\">Frontend</h4>\n <ul className=\"space-y-1 text-sm text-muted-foreground\">\n <li>Next.js 15</li>\n <li>React 18</li>\n <li>TypeScript</li>\n <li>Tailwind CSS</li>\n </ul>\n </div>\n <div>\n <h4 className=\"font-semibold text-foreground mb-2\">Backend</h4>\n <ul className=\"space-y-1 text-sm text-muted-foreground\">\n <li>Firebase Auth</li>\n <li>Firestore</li>\n <li>Firebase Hosting</li>\n </ul>\n </div>\n <div>\n <h4 className=\"font-semibold text-foreground mb-2\">APIs & Tools</h4>\n <ul className=\"space-y-1 text-sm text-muted-foreground\">\n <li>TMDB API</li>\n <li>shadcn/ui</li>\n <li>Radix UI</li>\n </ul>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n {/* Content Source */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n <div className=\"bg-gradient-to-r from-primary/10 to-primary/5 border border-primary/30 rounded-lg p-8\">\n <h3 className=\"text-2xl font-bold mb-4 text-foreground\">Our Content Partner</h3>\n <p className=\"text-muted-foreground mb-4 leading-relaxed\">\n StreamVerse sources all movie and TV show information from The Movie Database (TMDB), one of the most comprehensive and up-to-date entertainment databases available. We're grateful for their extensive API and the vast collection of content they maintain.\n </p>\n <p className=\"text-sm text-muted-foreground\">\n This product uses the TMDB API but is not endorsed or certified by TMDB.\n </p>\n </div>\n </div>\n </div>\n\n {/* Vision */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n <h2 className=\"text-3xl font-bold mb-8 text-foreground\">Our Vision</h2>\n <div className=\"space-y-6 text-muted-foreground leading-relaxed\">\n <p>\n We envision a future where streaming entertainment is accessible, personalized, and enjoyable for everyone. Our platform is designed to be intuitive and feature-rich, allowing users to easily discover new content and keep track of what they've watched.\n </p>\n <p>\n StreamVerse is constantly evolving. We're committed to adding new features, improving performance, and ensuring the best possible user experience. Your feedback helps us grow and improve every day.\n </p>\n </div>\n </div>\n </div>\n\n {/* Statistics */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n <h2 className=\"text-3xl font-bold mb-8 text-foreground text-center\">By The Numbers</h2>\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-8\">\n <div className=\"text-center\">\n <div className=\"text-4xl font-bold text-primary mb-2\">500K+</div>\n <p className=\"text-muted-foreground\">Movies & Shows</p>\n </div>\n <div className=\"text-center\">\n <div className=\"text-4xl font-bold text-primary mb-2\">1M+</div>\n <p className=\"text-muted-foreground\">User Ratings</p>\n </div>\n <div className=\"text-center\">\n <div className=\"text-4xl font-bold text-primary mb-2\">50K+</div>\n <p className=\"text-muted-foreground\">Watchlists Created</p>\n </div>\n </div>\n </div>\n </div>\n\n {/* CTA */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto text-center\">\n <div className=\"bg-card border border-border rounded-lg p-8\">\n <h3 className=\"text-2xl font-bold mb-4 text-foreground\">Ready to Explore?</h3>\n <p className=\"text-muted-foreground mb-8\">\n Join StreamVerse today and start discovering your next favorite movie or TV show.\n </p>\n <div className=\"flex flex-col sm:flex-row gap-4 justify-center\">\n <Link\n href=\"/\"\n className=\"px-8 py-3 bg-primary text-primary-foreground rounded-lg font-semibold hover:bg-primary/90 transition-colors\"\n >\n Explore Content\n </Link>\n <Link\n href=\"/help\"\n className=\"px-8 py-3 border border-border rounded-lg font-semibold hover:bg-muted transition-colors\"\n >\n Learn More\n </Link>\n </div>\n </div>\n </div>\n </div>\n\n {/* Footer Contact */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto text-center border-t pt-8\">\n <h4 className=\"font-semibold text-foreground mb-4\">Get in Touch</h4>\n <p className=\"text-muted-foreground mb-4\">\n Have questions? We'd love to hear from you.\n </p>\n <Link\n href=\"/contact\"\n className=\"inline-block text-primary hover:underline font-semibold\"\n >\n Contact Us\n </Link>\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\account\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\analytics\\page.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'firestore'. Either include it or remove the dependency array.","line":87,"column":6,"nodeType":"ArrayExpression","endLine":87,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [firestore]","fix":{"range":[2718,2720],"text":"[firestore]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect, useState } from 'react';\nimport { collection, getDocs, query, orderBy, limit } from 'firebase/firestore';\nimport { useFirebase } from '@/firebase';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';\nimport { Loader2 } from 'lucide-react';\n\ninterface ContentStats {\n title: string;\n type: 'movie' | 'tv';\n views: number;\n}\n\ninterface TypeDistribution {\n name: string;\n value: number;\n fill: string;\n}\n\nexport default function AdminAnalytics() {\n const { firestore } = useFirebase();\n const [topContent, setTopContent] = useState<ContentStats[]>([]);\n const [contentTypeDistribution, setContentTypeDistribution] = useState<TypeDistribution[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n const fetchAnalytics = async () => {\n try {\n const db = firestore;\n\n // Get all watches and count by content\n const contentViews = new Map<string, ContentStats>();\n let movieCount = 0;\n let tvCount = 0;\n\n const usersRef = collection(db, 'users');\n const usersSnap = await getDocs(usersRef);\n\n for (const userDoc of usersSnap.docs) {\n const userId = userDoc.id;\n const historyRef = collection(db, `users/${userId}/history`);\n const historySnap = await getDocs(historyRef);\n\n for (const historyDoc of historySnap.docs) {\n const data = historyDoc.data() as any;\n const key = `${data.type}-${data.contentId}`;\n\n if (contentViews.has(key)) {\n const existing = contentViews.get(key)!;\n existing.views++;\n } else {\n contentViews.set(key, {\n title: data.title || `Content ${data.contentId}`,\n type: data.type,\n views: 1,\n });\n\n if (data.type === 'movie') {\n movieCount++;\n } else {\n tvCount++;\n }\n }\n }\n }\n\n // Get top 10 content\n const topContentList = Array.from(contentViews.values())\n .sort((a, b) => b.views - a.views)\n .slice(0, 10);\n\n setTopContent(topContentList);\n setContentTypeDistribution([\n { name: 'Movies', value: movieCount, fill: '#3b82f6' },\n { name: 'TV Shows', value: tvCount, fill: '#10b981' },\n ]);\n } catch (error) {\n console.error('Error fetching analytics:', error);\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchAnalytics();\n }, []);\n\n const COLORS = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6'];\n\n return (\n <div>\n <h1 className=\"text-4xl font-bold mb-8\">Analytics</h1>\n\n {isLoading ? (\n <div className=\"flex justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : (\n <div className=\"space-y-8\">\n {/* Top Content */}\n <Card className=\"border border-border\">\n <CardHeader>\n <CardTitle>Top 10 Most Watched Content</CardTitle>\n </CardHeader>\n <CardContent>\n {topContent.length === 0 ? (\n <p className=\"text-muted-foreground text-center py-8\">No watch data available</p>\n ) : (\n <ResponsiveContainer width=\"100%\" height={400}>\n <BarChart data={topContent}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"title\" angle={-45} textAnchor=\"end\" height={100} />\n <YAxis />\n <Tooltip />\n <Legend />\n <Bar dataKey=\"views\" fill=\"#3b82f6\" name=\"Views\" />\n </BarChart>\n </ResponsiveContainer>\n )}\n </CardContent>\n </Card>\n\n {/* Content Type Distribution */}\n {contentTypeDistribution.length > 0 && (\n <Card className=\"border border-border\">\n <CardHeader>\n <CardTitle>Content Type Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"flex justify-center\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <PieChart>\n <Pie\n data={contentTypeDistribution}\n cx=\"50%\"\n cy=\"50%\"\n labelLine={false}\n label={({ name, value }) => `${name}: ${value}`}\n outerRadius={80}\n fill=\"#8884d8\"\n dataKey=\"value\"\n >\n {contentTypeDistribution.map((entry: any, index: number) => (\n <Cell key={`cell-${index}`} fill={entry.fill} />\n ))}\n </Pie>\n <Tooltip />\n </PieChart>\n </ResponsiveContainer>\n </CardContent>\n </Card>\n )}\n\n {/* Watch Details Table */}\n <Card className=\"border border-border\">\n <CardHeader>\n <CardTitle>Top Content Details</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border\">\n <th className=\"text-left px-4 py-2 font-semibold\">Title</th>\n <th className=\"text-left px-4 py-2 font-semibold\">Type</th>\n <th className=\"text-left px-4 py-2 font-semibold\">Views</th>\n </tr>\n </thead>\n <tbody>\n {topContent.map((content, index) => (\n <tr key={index} className=\"border-b border-border hover:bg-muted/50\">\n <td className=\"px-4 py-3\">{content.title}</td>\n <td className=\"px-4 py-3\">\n <span className={`px-2 py-1 rounded text-xs font-semibold ${\n content.type === 'movie' ? 'bg-blue-500/20 text-blue-500' : 'bg-green-500/20 text-green-500'\n }`}>\n {content.type === 'movie' ? 'Movie' : 'TV Show'}\n </span>\n </td>\n <td className=\"px-4 py-3\">{content.views}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\content\\page.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchContent'. Either include it or remove the dependency array.","line":63,"column":6,"nodeType":"ArrayExpression","endLine":63,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchContent]","fix":{"range":[2329,2331],"text":"[fetchContent]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useEffect } from 'react';\r\nimport { collection, getDocs, deleteDoc, doc, query, limit, orderBy } from 'firebase/firestore';\r\nimport { useFirebase } from '@/firebase';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Input } from '@/components/ui/input';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Loader2, Plus, Trash2, Search } from 'lucide-react';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport { ConfirmationDialog } from '@/components/admin/confirmation-dialog';\r\nimport { AddContentDialog } from '@/components/admin/add-content-dialog';\r\nimport { ContentDetailsDialog } from '@/components/admin/content-details-dialog';\r\nimport Image from 'next/image';\r\nimport { PLACEHOLDER_PERSON } from '@/lib/placeholder-urls';\r\n\r\ninterface ContentItem {\r\n id: number;\r\n title?: string;\r\n name?: string;\r\n type: 'movie' | 'tv';\r\n poster_path: string;\r\n vote_average: number;\r\n release_date?: string;\r\n first_air_date?: string;\r\n cachedAt?: any;\r\n}\r\n\r\ninterface ActorItem {\r\n id: number;\r\n name: string;\r\n profile_path: string;\r\n known_for_department?: string;\r\n popularity?: number;\r\n cachedAt?: any;\r\n}\r\n\r\nexport default function AdminContent() {\r\n const { firestore } = useFirebase();\r\n const { toast } = useToast();\r\n const [activeTab, setActiveTab] = useState('all');\r\n const [searchQuery, setSearchQuery] = useState('');\r\n const [movies, setMovies] = useState<ContentItem[]>([]);\r\n const [tvShows, setTVShows] = useState<ContentItem[]>([]);\r\n const [actors, setActors] = useState<ActorItem[]>([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [deleting, setDeleting] = useState<string | null>(null);\r\n const [addDialogOpen, setAddDialogOpen] = useState(false);\r\n const [confirmDialog, setConfirmDialog] = useState<{\r\n open: boolean;\r\n item: ContentItem | ActorItem | null;\r\n itemType: 'content' | 'actor';\r\n }>({ open: false, item: null, itemType: 'content' });\r\n const [detailsDialog, setDetailsDialog] = useState<{\r\n open: boolean;\r\n item: any | null;\r\n }>({ open: false, item: null });\r\n\r\n useEffect(() => {\r\n fetchContent();\r\n }, []);\r\n\r\n const fetchContent = async () => {\r\n setIsLoading(true);\r\n try {\r\n const db = firestore;\r\n\r\n const moviesRef = collection(db, 'movies');\r\n const moviesSnap = await getDocs(query(moviesRef, limit(100)));\r\n const moviesData = moviesSnap.docs.map(doc => ({\r\n ...doc.data(),\r\n id: parseInt(doc.id),\r\n type: 'movie' as const,\r\n })) as ContentItem[];\r\n\r\n const tvShowsRef = collection(db, 'tvShows');\r\n const tvShowsSnap = await getDocs(query(tvShowsRef, limit(100)));\r\n const tvShowsData = tvShowsSnap.docs.map(doc => ({\r\n ...doc.data(),\r\n id: parseInt(doc.id),\r\n type: 'tv' as const,\r\n })) as ContentItem[];\r\n\r\n const actorsRef = collection(db, 'persons');\r\n const actorsSnap = await getDocs(query(actorsRef, limit(100)));\r\n const actorsData = actorsSnap.docs.map(doc => ({\r\n ...doc.data(),\r\n id: parseInt(doc.id),\r\n })) as ActorItem[];\r\n\r\n setMovies(moviesData);\r\n setTVShows(tvShowsData);\r\n setActors(actorsData);\r\n } catch (error) {\r\n console.error('Error fetching content:', error);\r\n toast({\r\n title: 'Error',\r\n description: 'Failed to load content',\r\n variant: 'destructive',\r\n });\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n const openDeleteDialog = (item: ContentItem, e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n setConfirmDialog({ open: true, item, itemType: 'content' });\r\n };\r\n\r\n const openDeleteActorDialog = (item: ActorItem, e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n setConfirmDialog({ open: true, item, itemType: 'actor' });\r\n };\r\n\r\n const openDetailsDialog = async (item: ContentItem) => {\r\n setDetailsDialog({ open: true, item });\r\n };\r\n\r\n const handleDelete = async () => {\r\n if (!confirmDialog.item) return;\r\n\r\n const item = confirmDialog.item;\r\n const itemType = confirmDialog.itemType;\r\n setConfirmDialog({ open: false, item: null, itemType: 'content' });\r\n\r\n if (itemType === 'actor') {\r\n setDeleting(`actor-${item.id}`);\r\n try {\r\n const db = firestore;\r\n await deleteDoc(doc(db, 'persons', item.id.toString()));\r\n setActors(actors.filter(a => a.id !== item.id));\r\n toast({\r\n title: 'Deleted',\r\n description: `${(item as ActorItem).name} removed from database`,\r\n });\r\n } catch (error) {\r\n console.error('Error deleting actor:', error);\r\n toast({\r\n title: 'Error',\r\n description: 'Failed to delete actor',\r\n variant: 'destructive',\r\n });\r\n } finally {\r\n setDeleting(null);\r\n }\r\n return;\r\n }\r\n\r\n const contentItem = item as ContentItem;\r\n setDeleting(`${contentItem.type}-${contentItem.id}`);\r\n\r\n try {\r\n const db = firestore;\r\n const collectionName = contentItem.type === 'movie' ? 'movies' : 'tvShows';\r\n\r\n if (contentItem.type === 'tv') {\r\n const seasonsRef = collection(db, `tvShows/${contentItem.id}/seasons`);\r\n const seasonsSnap = await getDocs(seasonsRef);\r\n for (const seasonDoc of seasonsSnap.docs) {\r\n await deleteDoc(seasonDoc.ref);\r\n }\r\n }\r\n\r\n await deleteDoc(doc(db, collectionName, contentItem.id.toString()));\r\n\r\n if (contentItem.type === 'movie') {\r\n setMovies(movies.filter(m => m.id !== contentItem.id));\r\n } else {\r\n setTVShows(tvShows.filter(tv => tv.id !== contentItem.id));\r\n }\r\n\r\n toast({\r\n title: 'Deleted',\r\n description: `${contentItem.title || contentItem.name} removed from database`,\r\n });\r\n } catch (error) {\r\n console.error('Error deleting content:', error);\r\n toast({\r\n title: 'Error',\r\n description: 'Failed to delete content',\r\n variant: 'destructive',\r\n });\r\n } finally {\r\n setDeleting(null);\r\n }\r\n };\r\n\r\n const filterContent = (content: ContentItem[]) => {\r\n if (!searchQuery) return content;\r\n return content.filter(item =>\r\n (item.title || item.name || '').toLowerCase().includes(searchQuery.toLowerCase())\r\n );\r\n };\r\n\r\n const filterActors = (actorsList: ActorItem[]) => {\r\n if (!searchQuery) return actorsList;\r\n return actorsList.filter(actor =>\r\n actor.name.toLowerCase().includes(searchQuery.toLowerCase())\r\n );\r\n };\r\n\r\n const renderContentTable = (content: ContentItem[]) => {\r\n const filtered = filterContent(content);\r\n\r\n if (filtered.length === 0) {\r\n return (\r\n <div className=\"text-center py-12 text-muted-foreground\">\r\n {searchQuery ? `No results for \"${searchQuery}\"` : 'No content found. Use \"Add from TMDB\" to populate database.'}\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"overflow-x-auto\">\r\n <table className=\"w-full\">\r\n <thead>\r\n <tr className=\"border-b border-border\">\r\n <th className=\"text-left px-4 py-3 font-semibold\">Poster</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Title</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Type</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Year</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Rating</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Actions</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {filtered.map((item) => {\r\n const displayTitle = item.title || item.name || 'Unknown';\r\n const year = (item.release_date || item.first_air_date || '').slice(0, 4);\r\n const isDeleting = deleting === `${item.type}-${item.id}`;\r\n\r\n return (\r\n <tr\r\n key={`${item.type}-${item.id}`}\r\n className=\"border-b border-border hover:bg-muted/50 cursor-pointer\"\r\n onClick={() => openDetailsDialog(item)}\r\n >\r\n <td className=\"px-4 py-3\">\r\n <div className=\"relative w-12 h-16\">\r\n <Image\r\n src={item.poster_path}\r\n alt={displayTitle}\r\n fill\r\n className=\"object-cover rounded\"\r\n sizes=\"48px\"\r\n />\r\n </div>\r\n </td>\r\n <td className=\"px-4 py-3 font-medium\">{displayTitle}</td>\r\n <td className=\"px-4 py-3\">\r\n <Badge variant={item.type === 'movie' ? 'default' : 'secondary'}>\r\n {item.type === 'movie' ? 'Movie' : 'TV Show'}\r\n </Badge>\r\n </td>\r\n <td className=\"px-4 py-3 text-muted-foreground\">{year}</td>\r\n <td className=\"px-4 py-3\">\r\n <span className=\"flex items-center gap-1\">\r\n 苇蠁巍 {item.vote_average?.toFixed(1) || 'N/A'}\r\n </span>\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n <Button\r\n variant=\"destructive\"\r\n size=\"sm\"\r\n onClick={(e) => openDeleteDialog(item, e)}\r\n disabled={isDeleting}\r\n className=\"gap-1\"\r\n >\r\n {isDeleting ? (\r\n <Loader2 className=\"h-4 w-4 animate-spin\" />\r\n ) : (\r\n <Trash2 className=\"h-4 w-4\" />\r\n )}\r\n Delete\r\n </Button>\r\n </td>\r\n </tr>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n };\r\n\r\n const renderActorsTable = () => {\r\n const filtered = filterActors(actors);\r\n\r\n if (filtered.length === 0) {\r\n return (\r\n <div className=\"text-center py-12 text-muted-foreground\">\r\n {searchQuery ? `No results for \"${searchQuery}\"` : 'No actors found. Use \"Add from TMDB\" to populate database.'}\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"overflow-x-auto\">\r\n <table className=\"w-full\">\r\n <thead>\r\n <tr className=\"border-b border-border\">\r\n <th className=\"text-left px-4 py-3 font-semibold\">Photo</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Name</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Department</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Popularity</th>\r\n <th className=\"text-left px-4 py-3 font-semibold\">Actions</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {filtered.map((actor) => {\r\n const isDeleting = deleting === `actor-${actor.id}`;\r\n\r\n return (\r\n <tr\r\n key={`actor-${actor.id}`}\r\n className=\"border-b border-border hover:bg-muted/50\"\r\n >\r\n <td className=\"px-4 py-3\">\r\n <div className=\"relative w-12 h-16\">\r\n <Image\r\n src={actor.profile_path || PLACEHOLDER_PERSON}\r\n alt={actor.name}\r\n fill\r\n className=\"object-cover rounded\"\r\n sizes=\"48px\"\r\n />\r\n </div>\r\n </td>\r\n <td className=\"px-4 py-3 font-medium\">{actor.name}</td>\r\n <td className=\"px-4 py-3\">\r\n <Badge variant=\"outline\">\r\n {actor.known_for_department || 'Unknown'}\r\n </Badge>\r\n </td>\r\n <td className=\"px-4 py-3 text-muted-foreground\">\r\n {actor.popularity?.toFixed(1) || 'N/A'}\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n <Button\r\n variant=\"destructive\"\r\n size=\"sm\"\r\n onClick={(e) => openDeleteActorDialog(actor, e)}\r\n disabled={isDeleting}\r\n className=\"gap-1\"\r\n >\r\n {isDeleting ? (\r\n <Loader2 className=\"h-4 w-4 animate-spin\" />\r\n ) : (\r\n <Trash2 className=\"h-4 w-4\" />\r\n )}\r\n Delete\r\n </Button>\r\n </td>\r\n </tr>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n };\r\n\r\n const allContent = [...movies, ...tvShows].sort((a, b) =>\r\n (a.title || a.name || '').localeCompare(b.title || b.name || '')\r\n );\r\n\r\n return (\r\n <div>\r\n <div className=\"mb-8\">\r\n <h1 className=\"text-4xl font-bold\">Content Manager</h1>\r\n <p className=\"text-muted-foreground mt-2\">View, add, and manage movies, TV shows, and actors in your database</p>\r\n </div>\r\n\r\n <Card className=\"border border-border\">\r\n <CardHeader>\r\n <div className=\"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4\">\r\n <CardTitle>Database Content ({allContent.length + actors.length} items)</CardTitle>\r\n <div className=\"flex gap-2 w-full sm:w-auto\">\r\n <div className=\"relative flex-1 sm:flex-initial\">\r\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n placeholder=\"Search by title...\"\r\n value={searchQuery}\r\n onChange={(e) => setSearchQuery(e.target.value)}\r\n className=\"pl-9 w-full sm:w-64\"\r\n />\r\n </div>\r\n <Button onClick={() => setAddDialogOpen(true)} className=\"gap-2\">\r\n <Plus className=\"h-4 w-4\" />\r\n Add from TMDB\r\n </Button>\r\n </div>\r\n </div>\r\n </CardHeader>\r\n <CardContent>\r\n <Tabs value={activeTab} onValueChange={setActiveTab} className=\"space-y-4\">\r\n <TabsList className=\"grid w-full grid-cols-4 lg:w-[600px]\">\r\n <TabsTrigger value=\"all\">All ({allContent.length})</TabsTrigger>\r\n <TabsTrigger value=\"movies\">Movies ({movies.length})</TabsTrigger>\r\n <TabsTrigger value=\"tv\">TV Shows ({tvShows.length})</TabsTrigger>\r\n <TabsTrigger value=\"actors\">Actors ({actors.length})</TabsTrigger>\r\n </TabsList>\r\n\r\n <TabsContent value=\"all\">\r\n {isLoading ? (\r\n <div className=\"flex justify-center py-12\">\r\n <Loader2 className=\"h-8 w-8 animate-spin\" />\r\n </div>\r\n ) : (\r\n renderContentTable(allContent)\r\n )}\r\n </TabsContent>\r\n\r\n <TabsContent value=\"movies\">\r\n {isLoading ? (\r\n <div className=\"flex justify-center py-12\">\r\n <Loader2 className=\"h-8 w-8 animate-spin\" />\r\n </div>\r\n ) : (\r\n renderContentTable(movies)\r\n )}\r\n </TabsContent>\r\n\r\n <TabsContent value=\"tv\">\r\n {isLoading ? (\r\n <div className=\"flex justify-center py-12\">\r\n <Loader2 className=\"h-8 w-8 animate-spin\" />\r\n </div>\r\n ) : (\r\n renderContentTable(tvShows)\r\n )}\r\n </TabsContent>\r\n\r\n <TabsContent value=\"actors\">\r\n {isLoading ? (\r\n <div className=\"flex justify-center py-12\">\r\n <Loader2 className=\"h-8 w-8 animate-spin\" />\r\n </div>\r\n ) : (\r\n renderActorsTable()\r\n )}\r\n </TabsContent>\r\n </Tabs>\r\n </CardContent>\r\n </Card>\r\n\r\n <ConfirmationDialog\r\n open={confirmDialog.open}\r\n onOpenChange={(open) => setConfirmDialog({ ...confirmDialog, open })}\r\n onConfirm={handleDelete}\r\n title={confirmDialog.itemType === 'actor' ? 'Delete Actor' : 'Delete Content'}\r\n description={`Are you sure you want to delete \"${\r\n confirmDialog.itemType === 'actor'\r\n ? (confirmDialog.item as ActorItem)?.name\r\n : ((confirmDialog.item as ContentItem)?.title || (confirmDialog.item as ContentItem)?.name)\r\n }\"? This will remove it from the database.`}\r\n confirmText=\"Delete\"\r\n variant=\"destructive\"\r\n />\r\n\r\n <AddContentDialog\r\n open={addDialogOpen}\r\n onOpenChange={setAddDialogOpen}\r\n onSuccess={fetchContent}\r\n />\r\n\r\n <ContentDetailsDialog\r\n open={detailsDialog.open}\r\n onOpenChange={(open) => setDetailsDialog({ ...detailsDialog, open })}\r\n content={detailsDialog.item}\r\n />\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\settings\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\system-settings\\page.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'firestore'. Either include it or remove the dependency array.","line":133,"column":6,"nodeType":"ArrayExpression","endLine":133,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [firestore]","fix":{"range":[3666,3668],"text":"[firestore]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState } from 'react';\r\nimport { doc, getDoc, setDoc } from 'firebase/firestore';\r\nimport { useFirebase } from '@/firebase';\r\nimport { clearSettingsCache } from '@/lib/system-settings';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport { AlertCircle, Loader2, CheckCircle, Eye, EyeOff } from 'lucide-react';\r\n\r\ninterface ProjectSettings {\r\n ai: {\r\n geminiApiKey: string;\r\n geminiModel: '2.5-pro' | '2.5-flash' | '2.5-flash-lite';\r\n recommendationsCaching: boolean;\r\n recommendationsLimit: number;\r\n };\r\n tmdb: {\r\n apiKey: string;\r\n baseUrl: string;\r\n };\r\n branding: {\r\n siteName: string;\r\n tagline: string;\r\n primaryColor: string;\r\n supportEmail: string;\r\n };\r\n notifications: {\r\n enableEmailNotifications: boolean;\r\n notificationRecipient: string;\r\n digestFrequency: 'daily' | 'weekly' | 'monthly';\r\n };\r\n contentFiltering: {\r\n minRating: number;\r\n ageFilter: '0' | '13' | '16' | '18';\r\n blockedGenres: string[];\r\n restrictedRegions: string[];\r\n };\r\n userSettings: {\r\n sessionTimeout: number;\r\n maxLoginAttempts: number;\r\n passwordExpirationDays: number;\r\n emailVerificationRequired: boolean;\r\n };\r\n rateLimiting: {\r\n apiRateLimit: number;\r\n searchRateLimit: number;\r\n };\r\n analytics: {\r\n firebaseTrackingEnabled: boolean;\r\n firebaseTrackingId: string;\r\n dataRetentionDays: number;\r\n };\r\n}\r\n\r\nconst DEFAULT_SETTINGS: ProjectSettings = {\r\n ai: {\r\n geminiApiKey: '',\r\n geminiModel: '2.5-flash',\r\n recommendationsCaching: true,\r\n recommendationsLimit: 5,\r\n },\r\n tmdb: {\r\n apiKey: '',\r\n baseUrl: 'https://api.themoviedb.org/3',\r\n },\r\n branding: {\r\n siteName: 'StreamVerse',\r\n tagline: 'Your Streaming Universe',\r\n primaryColor: '#3b82f6',\r\n supportEmail: 'support@streamverse.com',\r\n },\r\n notifications: {\r\n enableEmailNotifications: true,\r\n notificationRecipient: '',\r\n digestFrequency: 'weekly',\r\n },\r\n contentFiltering: {\r\n minRating: 0,\r\n ageFilter: '0',\r\n blockedGenres: [],\r\n restrictedRegions: [],\r\n },\r\n userSettings: {\r\n sessionTimeout: 3600,\r\n maxLoginAttempts: 5,\r\n passwordExpirationDays: 90,\r\n emailVerificationRequired: true,\r\n },\r\n rateLimiting: {\r\n apiRateLimit: 1000,\r\n searchRateLimit: 30,\r\n },\r\n analytics: {\r\n firebaseTrackingEnabled: true,\r\n firebaseTrackingId: '',\r\n dataRetentionDays: 90,\r\n },\r\n};\r\n\r\nexport default function SystemSettingsPage() {\r\n const { firestore } = useFirebase();\r\n const [settings, setSettings] = useState<ProjectSettings>(DEFAULT_SETTINGS);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [saved, setSaved] = useState(false);\r\n const [error, setError] = useState('');\r\n const [showPasswords, setShowPasswords] = useState({\r\n geminiApiKey: false,\r\n tmdbApiKey: false,\r\n });\r\n\r\n useEffect(() => {\r\n const fetchSettings = async () => {\r\n try {\r\n const db = firestore;\r\n const docRef = doc(db, 'system', 'projectSettings');\r\n const docSnap = await getDoc(docRef);\r\n\r\n if (docSnap.exists()) {\r\n setSettings({ ...DEFAULT_SETTINGS, ...docSnap.data() } as ProjectSettings);\r\n }\r\n } catch (err) {\r\n console.error('Error fetching settings:', err);\r\n setError('Failed to load settings');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n fetchSettings();\r\n }, []);\r\n\r\n const handleSave = async () => {\r\n if (!settings.ai.geminiApiKey) {\r\n setError('Gemini API key is required');\r\n return;\r\n }\r\n if (!settings.tmdb.apiKey) {\r\n setError('TMDB API key is required');\r\n return;\r\n }\r\n if (!settings.branding.siteName || !settings.branding.supportEmail) {\r\n setError('Site name and support email are required');\r\n return;\r\n }\r\n\r\n setIsSaving(true);\r\n setError('');\r\n try {\r\n const db = firestore;\r\n const docRef = doc(db, 'system', 'projectSettings');\r\n await setDoc(docRef, settings);\r\n\r\n clearSettingsCache();\r\n\r\n setSaved(true);\r\n setTimeout(() => setSaved(false), 3000);\r\n } catch (err) {\r\n console.error('Error saving settings:', err);\r\n setError('Failed to save settings');\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n };\r\n\r\n const updateSetting = (path: string, value: any) => {\r\n setSettings(prev => {\r\n const keys = path.split('.');\r\n const newSettings = JSON.parse(JSON.stringify(prev));\r\n let current = newSettings;\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n current = current[keys[i]];\r\n }\r\n current[keys[keys.length - 1]] = value;\r\n return newSettings;\r\n });\r\n };\r\n\r\n if (isLoading) {\r\n return (\r\n <div className=\"flex justify-center py-12\">\r\n <Loader2 className=\"h-8 w-8 animate-spin\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"space-y-6\">\r\n <div>\r\n <h1 className=\"text-4xl font-bold mb-2\">System Settings</h1>\r\n <p className=\"text-muted-foreground\">Configure all system-wide settings and integrations</p>\r\n </div>\r\n\r\n {error && (\r\n <div className=\"bg-red-500/10 border border-red-500/50 rounded-lg p-4\">\r\n <p className=\"text-red-500 text-sm flex items-center gap-2\">\r\n <AlertCircle className=\"h-4 w-4\" />\r\n {error}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Card className=\"border border-border\">\r\n <CardHeader>\r\n <CardTitle>Configuration</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <Tabs defaultValue=\"ai\" className=\"space-y-6\">\r\n <TabsList className=\"grid w-full grid-cols-4 lg:grid-cols-8 gap-2\">\r\n <TabsTrigger value=\"ai\">AI</TabsTrigger>\r\n <TabsTrigger value=\"tmdb\">TMDB</TabsTrigger>\r\n <TabsTrigger value=\"branding\">Branding</TabsTrigger>\r\n <TabsTrigger value=\"notifications\">Notifications</TabsTrigger>\r\n <TabsTrigger value=\"filtering\">Filtering</TabsTrigger>\r\n <TabsTrigger value=\"users\">Users</TabsTrigger>\r\n <TabsTrigger value=\"rate\">Rate Limit</TabsTrigger>\r\n <TabsTrigger value=\"analytics\">Analytics</TabsTrigger>\r\n </TabsList>\r\n\r\n {/* AI Configuration Tab */}\r\n <TabsContent value=\"ai\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Gemini API Key</label>\r\n <div className=\"relative\">\r\n <input\r\n type={showPasswords.geminiApiKey ? 'text' : 'password'}\r\n value={settings.ai.geminiApiKey}\r\n onChange={(e) => updateSetting('ai.geminiApiKey', e.target.value)}\r\n placeholder=\"Enter your Gemini API key\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground pr-10\"\r\n />\r\n <button\r\n onClick={() => setShowPasswords(p => ({ ...p, geminiApiKey: !p.geminiApiKey }))}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\r\n >\r\n {showPasswords.geminiApiKey ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\r\n </button>\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">Get it from Google AI Studio</p>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Model</label>\r\n <select\r\n value={settings.ai.geminiModel}\r\n onChange={(e) => updateSetting('ai.geminiModel', e.target.value)}\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n >\r\n <option value=\"2.5-pro\">Gemini 2.5 Pro</option>\r\n <option value=\"2.5-flash\">Gemini 2.5 Flash</option>\r\n <option value=\"2.5-flash-lite\">Gemini 2.5 Flash Lite</option>\r\n </select>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"rec-caching\"\r\n checked={settings.ai.recommendationsCaching}\r\n onChange={(e) => updateSetting('ai.recommendationsCaching', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"rec-caching\" className=\"text-sm font-medium\">\r\n Enable recommendations caching\r\n </label>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Recommendations Limit</label>\r\n <input\r\n type=\"number\"\r\n value={settings.ai.recommendationsLimit}\r\n onChange={(e) => updateSetting('ai.recommendationsLimit', parseInt(e.target.value))}\r\n min=\"1\"\r\n max=\"50\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* TMDB Configuration Tab */}\r\n <TabsContent value=\"tmdb\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">API Key</label>\r\n <div className=\"relative\">\r\n <input\r\n type={showPasswords.tmdbApiKey ? 'text' : 'password'}\r\n value={settings.tmdb.apiKey}\r\n onChange={(e) => updateSetting('tmdb.apiKey', e.target.value)}\r\n placeholder=\"Enter your TMDB API key\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground pr-10\"\r\n />\r\n <button\r\n onClick={() => setShowPasswords(p => ({ ...p, tmdbApiKey: !p.tmdbApiKey }))}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\r\n >\r\n {showPasswords.tmdbApiKey ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Base URL</label>\r\n <input\r\n type=\"text\"\r\n value={settings.tmdb.baseUrl}\r\n onChange={(e) => updateSetting('tmdb.baseUrl', e.target.value)}\r\n placeholder=\"https://api.themoviedb.org/3\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Default: https://api.themoviedb.org/3</p>\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Branding Tab */}\r\n <TabsContent value=\"branding\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Site Name</label>\r\n <input\r\n type=\"text\"\r\n value={settings.branding.siteName}\r\n onChange={(e) => updateSetting('branding.siteName', e.target.value)}\r\n placeholder=\"StreamVerse\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Tagline</label>\r\n <input\r\n type=\"text\"\r\n value={settings.branding.tagline}\r\n onChange={(e) => updateSetting('branding.tagline', e.target.value)}\r\n placeholder=\"Your Streaming Universe\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Primary Color</label>\r\n <div className=\"flex gap-2\">\r\n <input\r\n type=\"color\"\r\n value={settings.branding.primaryColor}\r\n onChange={(e) => updateSetting('branding.primaryColor', e.target.value)}\r\n className=\"h-10 w-20 rounded-lg border border-border cursor-pointer\"\r\n />\r\n <input\r\n type=\"text\"\r\n value={settings.branding.primaryColor}\r\n onChange={(e) => updateSetting('branding.primaryColor', e.target.value)}\r\n placeholder=\"#3b82f6\"\r\n className=\"flex-1 px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Support Email</label>\r\n <input\r\n type=\"email\"\r\n value={settings.branding.supportEmail}\r\n onChange={(e) => updateSetting('branding.supportEmail', e.target.value)}\r\n placeholder=\"support@streamverse.com\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Notifications Tab */}\r\n <TabsContent value=\"notifications\" className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"enable-notifications\"\r\n checked={settings.notifications.enableEmailNotifications}\r\n onChange={(e) => updateSetting('notifications.enableEmailNotifications', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"enable-notifications\" className=\"text-sm font-medium\">\r\n Enable email notifications\r\n </label>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Notification Recipient Email</label>\r\n <input\r\n type=\"email\"\r\n value={settings.notifications.notificationRecipient}\r\n onChange={(e) => updateSetting('notifications.notificationRecipient', e.target.value)}\r\n placeholder=\"notifications@streamverse.com\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Digest Frequency</label>\r\n <select\r\n value={settings.notifications.digestFrequency}\r\n onChange={(e) => updateSetting('notifications.digestFrequency', e.target.value as any)}\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n >\r\n <option value=\"daily\">Daily</option>\r\n <option value=\"weekly\">Weekly</option>\r\n <option value=\"monthly\">Monthly</option>\r\n </select>\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Content Filtering Tab */}\r\n <TabsContent value=\"filtering\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Minimum Rating</label>\r\n <input\r\n type=\"number\"\r\n value={settings.contentFiltering.minRating}\r\n onChange={(e) => updateSetting('contentFiltering.minRating', parseFloat(e.target.value))}\r\n min=\"0\"\r\n max=\"10\"\r\n step=\"0.5\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Age Filter</label>\r\n <select\r\n value={settings.contentFiltering.ageFilter}\r\n onChange={(e) => updateSetting('contentFiltering.ageFilter', e.target.value as any)}\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n >\r\n <option value=\"0\">All Ages</option>\r\n <option value=\"13\">13+</option>\r\n <option value=\"16\">16+</option>\r\n <option value=\"18\">18+</option>\r\n </select>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Blocked Genres (comma-separated IDs)</label>\r\n <input\r\n type=\"text\"\r\n value={settings.contentFiltering.blockedGenres.join(',')}\r\n onChange={(e) => updateSetting('contentFiltering.blockedGenres', e.target.value.split(',').filter(g => g.trim()))}\r\n placeholder=\"27,53\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Restricted Regions (comma-separated codes)</label>\r\n <input\r\n type=\"text\"\r\n value={settings.contentFiltering.restrictedRegions.join(',')}\r\n onChange={(e) => updateSetting('contentFiltering.restrictedRegions', e.target.value.split(',').filter(r => r.trim()))}\r\n placeholder=\"KP,IR\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* User Settings Tab */}\r\n <TabsContent value=\"users\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Session Timeout (seconds)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.userSettings.sessionTimeout}\r\n onChange={(e) => updateSetting('userSettings.sessionTimeout', parseInt(e.target.value))}\r\n min=\"300\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Current: {Math.round(settings.userSettings.sessionTimeout / 60)} minutes</p>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Max Login Attempts</label>\r\n <input\r\n type=\"number\"\r\n value={settings.userSettings.maxLoginAttempts}\r\n onChange={(e) => updateSetting('userSettings.maxLoginAttempts', parseInt(e.target.value))}\r\n min=\"1\"\r\n max=\"10\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Password Expiration (days)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.userSettings.passwordExpirationDays}\r\n onChange={(e) => updateSetting('userSettings.passwordExpirationDays', parseInt(e.target.value))}\r\n min=\"0\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Set to 0 to disable expiration</p>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"email-verification\"\r\n checked={settings.userSettings.emailVerificationRequired}\r\n onChange={(e) => updateSetting('userSettings.emailVerificationRequired', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"email-verification\" className=\"text-sm font-medium\">\r\n Require email verification\r\n </label>\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Rate Limiting Tab */}\r\n <TabsContent value=\"rate\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">API Rate Limit (requests/hour per user)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.rateLimiting.apiRateLimit}\r\n onChange={(e) => updateSetting('rateLimiting.apiRateLimit', parseInt(e.target.value))}\r\n min=\"100\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Search Rate Limit (searches/hour per user)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.rateLimiting.searchRateLimit}\r\n onChange={(e) => updateSetting('rateLimiting.searchRateLimit', parseInt(e.target.value))}\r\n min=\"5\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Analytics Tab */}\r\n <TabsContent value=\"analytics\" className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"firebase-tracking\"\r\n checked={settings.analytics.firebaseTrackingEnabled}\r\n onChange={(e) => updateSetting('analytics.firebaseTrackingEnabled', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"firebase-tracking\" className=\"text-sm font-medium\">\r\n Enable Firebase Analytics\r\n </label>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Firebase Tracking ID</label>\r\n <input\r\n type=\"text\"\r\n value={settings.analytics.firebaseTrackingId}\r\n onChange={(e) => updateSetting('analytics.firebaseTrackingId', e.target.value)}\r\n placeholder=\"G-XXXXXXXXXX\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Found in Firebase Console</p>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Data Retention (days)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.analytics.dataRetentionDays}\r\n onChange={(e) => updateSetting('analytics.dataRetentionDays', parseInt(e.target.value))}\r\n min=\"1\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n </Tabs>\r\n\r\n <div className=\"mt-8 flex gap-2 items-center\">\r\n <Button\r\n onClick={handleSave}\r\n disabled={isSaving}\r\n className=\"gap-2\"\r\n >\r\n {isSaving ? (\r\n <Loader2 className=\"h-4 w-4 animate-spin\" />\r\n ) : (\r\n <CheckCircle className=\"h-4 w-4\" />\r\n )}\r\n Save Settings\r\n </Button>\r\n\r\n {saved && (\r\n <div className=\"flex items-center gap-2 text-green-500 text-sm\">\r\n <CheckCircle className=\"h-4 w-4\" />\r\n Saved successfully!\r\n </div>\r\n )}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <div className=\"bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 text-sm text-blue-500\">\r\n <p className=\"font-semibold mb-1\">Security Note</p>\r\n <p>All configuration settings are stored securely in Firestore. API keys are marked as sensitive fields.</p>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\system\\page.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchStats'. Either include it or remove the dependency array.","line":77,"column":6,"nodeType":"ArrayExpression","endLine":77,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchStats]","fix":{"range":[2487,2489],"text":"[fetchStats]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect, useState } from 'react';\nimport { collection, getDocs, writeBatch } from 'firebase/firestore';\nimport { useFirebase } from '@/firebase';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { AlertCircle, Trash2, Loader2, RefreshCw } from 'lucide-react';\nimport { ConfirmationDialog } from '@/components/admin/confirmation-dialog';\nimport { DeleteProgressDialog, DeleteTask } from '@/components/admin/delete-progress-dialog';\nimport { useToast } from '@/hooks/use-toast';\n\ninterface SystemStats {\n totalCachedMovies: number;\n totalCachedShows: number;\n totalCachedPersons: number;\n totalSearchCache: number;\n}\n\nexport default function AdminSystem() {\n const { firestore } = useFirebase();\n const { toast } = useToast();\n const [stats, setStats] = useState<SystemStats>({\n totalCachedMovies: 0,\n totalCachedShows: 0,\n totalCachedPersons: 0,\n totalSearchCache: 0,\n });\n const [isLoading, setIsLoading] = useState(true);\n const [clearing, setClearing] = useState<string | null>(null);\n const [confirmDialog, setConfirmDialog] = useState<{ open: boolean; type: string; title: string; description: string }>({\n open: false,\n type: '',\n title: '',\n description: '',\n });\n const [deleteTasks, setDeleteTasks] = useState<DeleteTask[]>([]);\n const [progressDialogOpen, setProgressDialogOpen] = useState(false);\n\n const fetchStats = async () => {\n setIsLoading(true);\n try {\n const db = firestore;\n\n const moviesRef = collection(db, 'movies');\n const moviesSnap = await getDocs(moviesRef);\n\n const showsRef = collection(db, 'tvShows');\n const showsSnap = await getDocs(showsRef);\n\n const personsRef = collection(db, 'persons');\n const personsSnap = await getDocs(personsRef);\n\n const cacheRef = collection(db, 'searchCache');\n const cacheSnap = await getDocs(cacheRef);\n\n setStats({\n totalCachedMovies: moviesSnap.size,\n totalCachedShows: showsSnap.size,\n totalCachedPersons: personsSnap.size,\n totalSearchCache: cacheSnap.size,\n });\n } catch (error) {\n console.error('Error fetching system stats:', error);\n toast({\n title: 'Error',\n description: 'Failed to load cache statistics',\n variant: 'destructive',\n });\n } finally {\n setIsLoading(false);\n }\n };\n\n useEffect(() => {\n fetchStats();\n }, []);\n\n const openConfirmDialog = (type: string) => {\n const labels: Record<string, { title: string; description: string }> = {\n movies: {\n title: 'Clear Movies Cache',\n description: `This will permanently delete ${stats.totalCachedMovies} cached movies. They will be re-cached when accessed again.`,\n },\n tvshows: {\n title: 'Clear TV Shows Cache',\n description: `This will permanently delete ${stats.totalCachedShows} cached TV shows. They will be re-cached when accessed again.`,\n },\n persons: {\n title: 'Clear Persons Cache',\n description: `This will permanently delete ${stats.totalCachedPersons} cached persons. They will be re-cached when accessed again.`,\n },\n search: {\n title: 'Clear Search Cache',\n description: `This will permanently delete ${stats.totalSearchCache} search queries. They will be re-cached when searched again.`,\n },\n };\n\n setConfirmDialog({\n open: true,\n type,\n title: labels[type]?.title || 'Clear Cache',\n description: labels[type]?.description || 'This action cannot be undone.',\n });\n };\n\n const clearCache = async () => {\n const cacheType = confirmDialog.type;\n setConfirmDialog({ ...confirmDialog, open: false });\n setClearing(cacheType);\n\n try {\n const db = firestore;\n let collectionName = '';\n let deletedCount = 0;\n let taskType: 'movie' | 'tv' | 'person' | 'search' = 'movie';\n let taskLabel = '';\n\n switch (cacheType) {\n case 'movies':\n collectionName = 'movies';\n deletedCount = stats.totalCachedMovies;\n taskType = 'movie';\n taskLabel = 'Deleting movies cache';\n break;\n case 'tvshows':\n collectionName = 'tvShows';\n deletedCount = stats.totalCachedShows;\n taskType = 'tv';\n taskLabel = 'Deleting TV shows cache';\n break;\n case 'persons':\n collectionName = 'persons';\n deletedCount = stats.totalCachedPersons;\n taskType = 'person';\n taskLabel = 'Deleting persons cache';\n break;\n case 'search':\n collectionName = 'searchCache';\n deletedCount = stats.totalSearchCache;\n taskType = 'search';\n taskLabel = 'Deleting search cache';\n break;\n default:\n return;\n }\n\n const tasks: DeleteTask[] = [{\n id: cacheType,\n label: taskLabel,\n type: taskType,\n status: 'pending',\n total: deletedCount,\n current: 0\n }];\n\n setDeleteTasks(tasks);\n setProgressDialogOpen(true);\n\n await new Promise(resolve => setTimeout(resolve, 50));\n\n setDeleteTasks(prev => prev.map(t => ({ ...t, status: 'deleting' as const })));\n\n const ref = collection(db, collectionName);\n const snaps = await getDocs(ref);\n\n // Delete in batches of 500 (Firestore limit)\n const batchSize = 500;\n const docs = snaps.docs;\n\n for (let i = 0; i < docs.length; i += batchSize) {\n const batch = writeBatch(db);\n const chunk = docs.slice(i, i + batchSize);\n\n chunk.forEach((doc) => {\n batch.delete(doc.ref);\n });\n\n await batch.commit();\n\n const deletedSoFar = Math.min(i + batchSize, docs.length);\n setDeleteTasks(prev => prev.map(t => ({ ...t, current: deletedSoFar })));\n }\n\n setDeleteTasks(prev => prev.map(t => ({ ...t, status: 'completed' as const })));\n\n setStats(prev => ({\n ...prev,\n ...(cacheType === 'movies' && { totalCachedMovies: 0 }),\n ...(cacheType === 'tvshows' && { totalCachedShows: 0 }),\n ...(cacheType === 'persons' && { totalCachedPersons: 0 }),\n ...(cacheType === 'search' && { totalSearchCache: 0 }),\n }));\n\n toast({\n title: 'Cache Cleared',\n description: `Successfully deleted ${deletedCount} items from ${cacheType} cache.`,\n });\n } catch (error) {\n console.error(`Error clearing ${cacheType} cache:`, error);\n setDeleteTasks([]);\n setProgressDialogOpen(false);\n toast({\n title: 'Error',\n description: `Failed to clear ${cacheType} cache. Please try again.`,\n variant: 'destructive',\n });\n } finally {\n setClearing(null);\n }\n };\n\n const handleProgressComplete = () => {\n setProgressDialogOpen(false);\n setDeleteTasks([]);\n };\n\n return (\n <div>\n <div className=\"flex justify-between items-center mb-8\">\n <h1 className=\"text-4xl font-bold\">System Health</h1>\n <Button\n variant=\"outline\"\n onClick={fetchStats}\n disabled={isLoading}\n className=\"gap-2\"\n >\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <RefreshCw className=\"h-4 w-4\" />\n )}\n Refresh Stats\n </Button>\n </div>\n\n {/* Cache Stats */}\n <Card className=\"border border-border mb-8\">\n <CardHeader>\n <CardTitle>Cache Statistics</CardTitle>\n </CardHeader>\n <CardContent>\n {isLoading ? (\n <div className=\"flex justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin\" />\n </div>\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalCachedMovies}</p>\n <p className=\"text-sm text-muted-foreground\">Cached Movies</p>\n </div>\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalCachedShows}</p>\n <p className=\"text-sm text-muted-foreground\">Cached TV Shows</p>\n </div>\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalCachedPersons}</p>\n <p className=\"text-sm text-muted-foreground\">Cached Persons</p>\n </div>\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalSearchCache}</p>\n <p className=\"text-sm text-muted-foreground\">Search Cache</p>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Cache Management */}\n <Card className=\"border border-border\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <AlertCircle className=\"h-5 w-5 text-yellow-500\" />\n Cache Management\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-muted-foreground mb-6\">\n Clear cache to free up Firestore space. Cache will be rebuilt automatically when needed.\n </p>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('movies')}\n disabled={clearing === 'movies' || stats.totalCachedMovies === 0}\n className=\"gap-2\"\n >\n {clearing === 'movies' ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Clear Movies Cache\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('tvshows')}\n disabled={clearing === 'tvshows' || stats.totalCachedShows === 0}\n className=\"gap-2\"\n >\n {clearing === 'tvshows' ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Clear TV Shows Cache\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('persons')}\n disabled={clearing === 'persons' || stats.totalCachedPersons === 0}\n className=\"gap-2\"\n >\n {clearing === 'persons' ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Clear Persons Cache\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('search')}\n disabled={clearing === 'search' || stats.totalSearchCache === 0}\n className=\"gap-2\"\n >\n {clearing === 'search' ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Trash2 className=\"h-4 w-4\" />\n )}\n Clear Search Cache\n </Button>\n </div>\n </CardContent>\n </Card>\n\n {/* Confirmation Dialog */}\n <ConfirmationDialog\n open={confirmDialog.open}\n onOpenChange={(open) => setConfirmDialog({ ...confirmDialog, open })}\n onConfirm={clearCache}\n title={confirmDialog.title}\n description={confirmDialog.description}\n confirmText=\"Clear Cache\"\n cancelText=\"Cancel\"\n variant=\"destructive\"\n />\n\n {/* Progress Dialog */}\n <DeleteProgressDialog\n open={progressDialogOpen}\n tasks={deleteTasks}\n onComplete={handleProgressComplete}\n />\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\admin\\users\\page.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchUsers'. Either include it or remove the dependency array.","line":71,"column":6,"nodeType":"ArrayExpression","endLine":71,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchUsers]","fix":{"range":[2469,2471],"text":"[fetchUsers]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState } from 'react';\r\nimport { collection, getDocs, doc, updateDoc } from 'firebase/firestore';\r\nimport { useFirebase } from '@/firebase';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Badge } from '@/components/ui/badge';\r\nimport { Shield, Loader2, UserCog, RefreshCw } from 'lucide-react';\r\nimport { ConfirmationDialog } from '@/components/admin/confirmation-dialog';\r\nimport { useToast } from '@/hooks/use-toast';\r\nimport type { UserProfile } from '@/lib/types';\r\n\r\nexport default function AdminUsers() {\r\n const { firestore } = useFirebase();\r\n const { toast } = useToast();\r\n const [users, setUsers] = useState<(UserProfile & { watchCount: number; ratingCount: number })[]>([]);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [updating, setUpdating] = useState<string | null>(null);\r\n const [confirmDialog, setConfirmDialog] = useState<{ open: boolean; userId: string; currentStatus: boolean; userEmail: string }>({\r\n open: false,\r\n userId: '',\r\n currentStatus: false,\r\n userEmail: '',\r\n });\r\n\r\n const fetchUsers = async () => {\r\n setIsLoading(true);\r\n try {\r\n const db = firestore;\r\n const usersRef = collection(db, 'users');\r\n const usersSnap = await getDocs(usersRef);\r\n\r\n const usersData = [];\r\n\r\n for (const userDoc of usersSnap.docs) {\r\n const userData = userDoc.data() as any;\r\n const userId = userDoc.id;\r\n\r\n const historyRef = collection(db, `users/${userId}/history`);\r\n const historySnap = await getDocs(historyRef);\r\n\r\n const ratingsRef = collection(db, `users/${userId}/ratings`);\r\n const ratingsSnap = await getDocs(ratingsRef);\r\n\r\n usersData.push({\r\n id: userId,\r\n email: userData.email || 'N/A',\r\n createdAt: userData.createdAt || 0,\r\n isAdmin: userData.isAdmin === true,\r\n watchCount: historySnap.size,\r\n ratingCount: ratingsSnap.size,\r\n });\r\n }\r\n\r\n setUsers(usersData.sort((a, b) => b.createdAt - a.createdAt));\r\n } catch (error) {\r\n console.error('Error fetching users:', error);\r\n toast({\r\n title: 'Error',\r\n description: 'Failed to load users',\r\n variant: 'destructive',\r\n });\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n fetchUsers();\r\n }, []);\r\n\r\n const openConfirmDialog = (userId: string, currentStatus: boolean, userEmail: string) => {\r\n setConfirmDialog({ open: true, userId, currentStatus, userEmail });\r\n };\r\n\r\n const toggleAdmin = async () => {\r\n const { userId, currentStatus, userEmail } = confirmDialog;\r\n setConfirmDialog({ ...confirmDialog, open: false });\r\n setUpdating(userId);\r\n\r\n try {\r\n const db = firestore;\r\n const userRef = doc(db, 'users', userId);\r\n await updateDoc(userRef, { isAdmin: !currentStatus });\r\n\r\n setUsers(users.map(u =>\r\n u.id === userId ? { ...u, isAdmin: !currentStatus } : u\r\n ));\r\n\r\n toast({\r\n title: 'Success',\r\n description: `${currentStatus ? 'Revoked' : 'Granted'} admin access for ${userEmail}`,\r\n });\r\n } catch (error) {\r\n console.error('Error updating admin status:', error);\r\n toast({\r\n title: 'Error',\r\n description: 'Failed to update admin status',\r\n variant: 'destructive',\r\n });\r\n } finally {\r\n setUpdating(null);\r\n }\r\n };\r\n\r\n return (\r\n <div>\r\n <div className=\"flex justify-between items-center mb-8\">\r\n <h1 className=\"text-4xl font-bold\">User Management</h1>\r\n <Button variant=\"outline\" onClick={fetchUsers} disabled={isLoading} className=\"gap-2\">\r\n {isLoading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\r\n Refresh\r\n </Button>\r\n </div>\r\n\r\n <Card className=\"border border-border\">\r\n <CardHeader>\r\n <CardTitle>All Users ({users.length})</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n {isLoading ? (\r\n <div className=\"flex justify-center py-8\">\r\n <Loader2 className=\"h-6 w-6 animate-spin\" />\r\n </div>\r\n ) : users.length === 0 ? (\r\n <p className=\"text-muted-foreground text-center py-8\">No users found</p>\r\n ) : (\r\n <div className=\"overflow-x-auto\">\r\n <table className=\"w-full\">\r\n <thead>\r\n <tr className=\"border-b border-border\">\r\n <th className=\"text-left px-4 py-2 font-semibold\">Email</th>\r\n <th className=\"text-left px-4 py-2 font-semibold\">Watches</th>\r\n <th className=\"text-left px-4 py-2 font-semibold\">Ratings</th>\r\n <th className=\"text-left px-4 py-2 font-semibold\">Joined</th>\r\n <th className=\"text-left px-4 py-2 font-semibold\">Admin</th>\r\n <th className=\"text-left px-4 py-2 font-semibold\">Actions</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n {users.map((user) => (\r\n <tr key={user.id} className=\"border-b border-border hover:bg-muted/50\">\r\n <td className=\"px-4 py-3\">{user.email}</td>\r\n <td className=\"px-4 py-3\">{user.watchCount}</td>\r\n <td className=\"px-4 py-3\">{user.ratingCount}</td>\r\n <td className=\"px-4 py-3 text-sm text-muted-foreground\">\r\n {new Date(user.createdAt).toLocaleDateString()}\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n <Badge variant={user.isAdmin ? 'default' : 'secondary'}>\r\n {user.isAdmin ? 'Admin' : 'User'}\r\n </Badge>\r\n </td>\r\n <td className=\"px-4 py-3\">\r\n <Button\r\n variant=\"outline\"\r\n size=\"sm\"\r\n onClick={() => openConfirmDialog(user.id, user.isAdmin ?? false, user.email)}\r\n disabled={updating === user.id}\r\n className=\"gap-1\"\r\n >\r\n {updating === user.id ? (\r\n <Loader2 className=\"h-4 w-4 animate-spin\" />\r\n ) : (\r\n <Shield className=\"h-4 w-4\" />\r\n )}\r\n {user.isAdmin ? 'Revoke' : 'Grant'}\r\n </Button>\r\n </td>\r\n </tr>\r\n ))}\r\n </tbody>\r\n </table>\r\n </div>\r\n )}\r\n </CardContent>\r\n </Card>\r\n\r\n <ConfirmationDialog\r\n open={confirmDialog.open}\r\n onOpenChange={(open) => setConfirmDialog({ ...confirmDialog, open })}\r\n onConfirm={toggleAdmin}\r\n title={confirmDialog.currentStatus ? 'Revoke Admin Access' : 'Grant Admin Access'}\r\n description={`Are you sure you want to ${confirmDialog.currentStatus ? 'revoke admin access from' : 'grant admin access to'} ${confirmDialog.userEmail}?`}\r\n confirmText={confirmDialog.currentStatus ? 'Revoke' : 'Grant'}\r\n variant={confirmDialog.currentStatus ? 'destructive' : 'default'}\r\n />\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\api\\cache\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\api\\contact\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\api\\genre\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\contact\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":58,"column":15,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[1782,1861],"text":"\n We'd love to hear from you. Get in touch with our team.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[1782,1861],"text":"\n We‘d love to hear from you. Get in touch with our team.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[1782,1861],"text":"\n We'd love to hear from you. Get in touch with our team.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[1782,1861],"text":"\n We’d love to hear from you. Get in touch with our team.\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":117,"column":63,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[4528,4586],"text":"We've received your inquiry and will get back to you soon."},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[4528,4586],"text":"We‘ve received your inquiry and will get back to you soon."},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[4528,4586],"text":"We've received your inquiry and will get back to you soon."},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[4528,4586],"text":"We’ve received your inquiry and will get back to you soon."},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState } from 'react';\nimport { Mail, Phone, MapPin, Send, Loader2 } from 'lucide-react';\n\nexport default function ContactUs() {\n const [formData, setFormData] = useState({\n name: '',\n email: '',\n subject: '',\n message: ''\n });\n const [submitted, setSubmitted] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState('');\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {\n const { name, value } = e.target;\n setFormData(prev => ({ ...prev, [name]: value }));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsLoading(true);\n setError('');\n\n try {\n const response = await fetch('/api/contact', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(formData),\n });\n\n if (!response.ok) {\n const data = await response.json();\n throw new Error(data.error || 'Failed to send message');\n }\n\n setSubmitted(true);\n setTimeout(() => {\n setFormData({ name: '', email: '', subject: '', message: '' });\n setSubmitted(false);\n }, 3000);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to send message');\n } finally {\n setIsLoading(false);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-gradient-to-b from-background to-background/80\">\n {/* Header */}\n <div className=\"bg-gradient-to-r from-primary/20 to-primary/5 py-12 border-b\">\n <div className=\"container mx-auto px-4\">\n <h1 className=\"text-4xl font-bold mb-4\">Contact Us</h1>\n <p className=\"text-lg text-muted-foreground\">\n We'd love to hear from you. Get in touch with our team.\n </p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-8 max-w-6xl mx-auto\">\n\n {/* Contact Info */}\n <div className=\"space-y-8\">\n <div>\n <h3 className=\"text-xl font-bold mb-4\">Get in Touch</h3>\n <div className=\"space-y-6\">\n <div className=\"flex gap-4\">\n <Mail className=\"h-6 w-6 text-primary flex-shrink-0 mt-1\" />\n <div>\n <p className=\"font-semibold text-foreground\">Email</p>\n <p className=\"text-muted-foreground\">support@streamverse.com</p>\n <p className=\"text-sm text-muted-foreground mt-1\">We typically respond within 24 hours</p>\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <Phone className=\"h-6 w-6 text-primary flex-shrink-0 mt-1\" />\n <div>\n <p className=\"font-semibold text-foreground\">Phone</p>\n <p className=\"text-muted-foreground\">+1 (555) 123-4567</p>\n <p className=\"text-sm text-muted-foreground mt-1\">Monday to Friday, 9AM - 5PM PST</p>\n </div>\n </div>\n\n <div className=\"flex gap-4\">\n <MapPin className=\"h-6 w-6 text-primary flex-shrink-0 mt-1\" />\n <div>\n <p className=\"font-semibold text-foreground\">Office</p>\n <p className=\"text-muted-foreground\">Los Angeles, CA 90001</p>\n <p className=\"text-sm text-muted-foreground mt-1\">United States</p>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"bg-muted rounded-lg p-6 border border-border\">\n <h4 className=\"font-semibold mb-2\">Response Time</h4>\n <p className=\"text-sm text-muted-foreground\">\n Our support team aims to respond to all inquiries within 24 business hours.\n </p>\n </div>\n </div>\n\n {/* Contact Form */}\n <div className=\"lg:col-span-2\">\n <div className=\"bg-card border border-border rounded-lg p-8\">\n <h3 className=\"text-2xl font-bold mb-6\">Send us a Message</h3>\n\n {submitted ? (\n <div className=\"bg-green-500/10 border border-green-500/50 rounded-lg p-6 text-center\">\n <p className=\"text-green-500 font-semibold\">Thank you for your message!</p>\n <p className=\"text-muted-foreground mt-2\">We've received your inquiry and will get back to you soon.</p>\n </div>\n ) : (\n <>\n {error && (\n <div className=\"bg-red-500/10 border border-red-500/50 rounded-lg p-4 mb-6\">\n <p className=\"text-red-500 text-sm\">{error}</p>\n </div>\n )}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-semibold mb-2 text-foreground\">\n Full Name *\n </label>\n <input\n type=\"text\"\n id=\"name\"\n name=\"name\"\n value={formData.name}\n onChange={handleChange}\n required\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\n placeholder=\"Your name\"\n />\n </div>\n\n <div>\n <label htmlFor=\"email\" className=\"block text-sm font-semibold mb-2 text-foreground\">\n Email Address *\n </label>\n <input\n type=\"email\"\n id=\"email\"\n name=\"email\"\n value={formData.email}\n onChange={handleChange}\n required\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\n placeholder=\"your@email.com\"\n />\n </div>\n\n <div>\n <label htmlFor=\"subject\" className=\"block text-sm font-semibold mb-2 text-foreground\">\n Subject *\n </label>\n <select\n id=\"subject\"\n name=\"subject\"\n value={formData.subject}\n onChange={handleChange}\n required\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\n >\n <option value=\"\">Select a subject</option>\n <option value=\"bug\">Bug Report</option>\n <option value=\"feature\">Feature Request</option>\n <option value=\"feedback\">General Feedback</option>\n <option value=\"account\">Account Issue</option>\n <option value=\"partnership\">Partnership Inquiry</option>\n <option value=\"other\">Other</option>\n </select>\n </div>\n\n <div>\n <label htmlFor=\"message\" className=\"block text-sm font-semibold mb-2 text-foreground\">\n Message *\n </label>\n <textarea\n id=\"message\"\n name=\"message\"\n value={formData.message}\n onChange={handleChange}\n required\n rows={6}\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground resize-none\"\n placeholder=\"Tell us how we can help...\"\n />\n </div>\n\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"w-full bg-primary text-primary-foreground py-2 rounded-lg font-semibold hover:bg-primary/90 transition-colors flex items-center justify-center gap-2 disabled:opacity-50\"\n >\n {isLoading ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n Sending...\n </>\n ) : (\n <>\n <Send className=\"h-4 w-4\" />\n Send Message\n </>\n )}\n </button>\n </form>\n </>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\genre\\[type]\\[id]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\help\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":134,"column":16,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5521,5620],"text":"\n Can't find the answer you're looking for? Our support team is here to help.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[5521,5620],"text":"\n Can‘t find the answer you're looking for? Our support team is here to help.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5521,5620],"text":"\n Can't find the answer you're looking for? Our support team is here to help.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[5521,5620],"text":"\n Can’t find the answer you're looking for? Our support team is here to help.\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":134,"column":38,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5521,5620],"text":"\n Can't find the answer you're looking for? Our support team is here to help.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[5521,5620],"text":"\n Can't find the answer you‘re looking for? Our support team is here to help.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5521,5620],"text":"\n Can't find the answer you're looking for? Our support team is here to help.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[5521,5620],"text":"\n Can't find the answer you’re looking for? Our support team is here to help.\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Link from 'next/link';\nimport { ChevronRight, Mail, MessageSquare, Search } from 'lucide-react';\n\nexport default function HelpCenter() {\n const faqs = [\n {\n category: \"Account & Sign In\",\n items: [\n {\n q: \"How do I create a StreamVerse account?\",\n a: \"Click on the Sign Up button, enter your email and password, or use Google OAuth for quick registration. Verify your email and you're ready to start!\"\n },\n {\n q: \"How do I reset my password?\",\n a: \"Click 'Forgot Password' on the login page, enter your email, and we'll send you a reset link. Follow the instructions to create a new password.\"\n },\n {\n q: \"Can I use multiple devices with one account?\",\n a: \"Yes! You can log in to your StreamVerse account on multiple devices simultaneously.\"\n }\n ]\n },\n {\n category: \"Browsing & Searching\",\n items: [\n {\n q: \"How do I search for movies or TV shows?\",\n a: \"Use the search bar at the top of the page. Type the title, actor name, or keyword to find what you're looking for.\"\n },\n {\n q: \"What do the genre categories mean?\",\n a: \"Genres help you discover content based on type. Click any genre to see all movies and TV shows in that category.\"\n },\n {\n q: \"How are movies sorted by trending?\",\n a: \"Trending content is based on popularity and views from The Movie Database (TMDB) in real-time.\"\n }\n ]\n },\n {\n category: \"Watchlist & Ratings\",\n items: [\n {\n q: \"How do I add content to my watchlist?\",\n a: \"Click the heart icon on any movie or TV show to add it to your watchlist. Access your list anytime from the navigation menu.\"\n },\n {\n q: \"Can I rate movies and TV shows?\",\n a: \"Yes! Click on a title to view details, then use the star rating system to rate the content. Your ratings help personalize your experience.\"\n },\n {\n q: \"How do I see my viewing history?\",\n a: \"Your viewing history is automatically tracked when you watch content. You can view it in your account settings.\"\n }\n ]\n },\n {\n category: \"Content & Trailers\",\n items: [\n {\n q: \"Can I watch trailers?\",\n a: \"Yes! Click the 'Play Trailer' button on any content detail page to watch the trailer in a popup player.\"\n },\n {\n q: \"What sources does StreamVerse use for content information?\",\n a: \"We get all movie and TV show information from The Movie Database (TMDB), a reliable and comprehensive entertainment database.\"\n },\n {\n q: \"How often is new content added?\",\n a: \"Our content is updated in real-time from TMDB, so you always have access to the latest movies and TV shows.\"\n }\n ]\n }\n ];\n\n return (\n <div className=\"min-h-screen bg-gradient-to-b from-background to-background/80\">\n {/* Header */}\n <div className=\"bg-gradient-to-r from-primary/20 to-primary/5 py-12 border-b\">\n <div className=\"container mx-auto px-4\">\n <h1 className=\"text-4xl font-bold mb-4\">Help Center</h1>\n <p className=\"text-lg text-muted-foreground\">\n Find answers to common questions about StreamVerse\n </p>\n </div>\n </div>\n\n {/* Search Bar */}\n <div className=\"sticky top-0 z-40 bg-background/95 backdrop-blur border-b py-4\">\n <div className=\"container mx-auto px-4\">\n <div className=\"relative\">\n <Search className=\"absolute left-4 top-3 h-5 w-5 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Search for answers...\"\n className=\"w-full pl-12 pr-4 py-2 rounded-lg bg-muted border border-border focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n </div>\n </div>\n\n {/* Main Content */}\n <div className=\"container mx-auto px-4 py-12\">\n <div className=\"max-w-4xl mx-auto\">\n {faqs.map((section) => (\n <div key={section.category} className=\"mb-12\">\n <h2 className=\"text-2xl font-bold mb-6 text-foreground\">\n {section.category}\n </h2>\n <div className=\"space-y-4\">\n {section.items.map((item, idx) => (\n <details\n key={idx}\n className=\"group border border-border rounded-lg hover:border-primary/50 transition-colors\"\n >\n <summary className=\"flex items-center justify-between w-full p-4 cursor-pointer font-semibold text-foreground hover:text-primary transition-colors\">\n <span>{item.q}</span>\n <ChevronRight className=\"h-5 w-5 text-muted-foreground group-open:rotate-90 transition-transform\" />\n </summary>\n <div className=\"px-4 pb-4 text-muted-foreground border-t border-border/50 pt-4\">\n {item.a}\n </div>\n </details>\n ))}\n </div>\n </div>\n ))}\n </div>\n\n {/* Contact Section */}\n <div className=\"max-w-4xl mx-auto mt-16 p-8 bg-muted rounded-lg border border-border\">\n <h3 className=\"text-xl font-bold mb-4\">Still need help?</h3>\n <p className=\"text-muted-foreground mb-6\">\n Can't find the answer you're looking for? Our support team is here to help.\n </p>\n <div className=\"flex flex-col sm:flex-row gap-4\">\n <Link\n href=\"/contact\"\n className=\"flex items-center justify-center gap-2 px-6 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Mail className=\"h-4 w-4\" />\n Contact Us\n </Link>\n <Link\n href=\"/contact\"\n className=\"flex items-center justify-center gap-2 px-6 py-2 border border-border rounded-lg hover:bg-muted transition-colors\"\n >\n <MessageSquare className=\"h-4 w-4\" />\n Send a Message\n </Link>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\in-cinemas\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\login\\page.tsx","messages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":153,"column":13,"nodeType":"JSXOpeningElement","endLine":157,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\n\"use client\";\n\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport * as z from \"zod\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport Link from \"next/link\";\nimport { useAuth } from \"@/firebase\";\nimport { useRouter } from \"next/navigation\";\nimport { useToast } from \"@/hooks/use-toast\";\nimport { useEffect } from \"react\";\nimport { useUser } from \"@/firebase/provider\";\nimport { signInWithEmailAndPassword, signInWithPopup, GoogleAuthProvider } from \"firebase/auth\";\n\nconst formSchema = z.object({\n email: z.string().email({ message: \"Please enter a valid email.\" }),\n password: z.string().min(1, { message: \"Password is required.\" }),\n});\n\nexport default function LoginPage() {\n const auth = useAuth();\n const router = useRouter();\n const { toast } = useToast();\n const { user, isUserLoading } = useUser();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n email: \"\",\n password: \"\",\n },\n });\n\n useEffect(() => {\n if (!isUserLoading && user) {\n router.push(\"/\");\n }\n }, [user, isUserLoading, router]);\n\n const onSubmit = async (values: z.infer<typeof formSchema>) => {\n try {\n await signInWithEmailAndPassword(auth, values.email, values.password);\n toast({\n title: \"Signing in...\",\n description: \"You will be redirected shortly.\",\n });\n } catch (error: any) {\n toast({\n variant: \"destructive\",\n title: \"Authentication Error\",\n description: error.message,\n });\n }\n };\n\n const handleGoogleSignIn = async () => {\n try {\n const provider = new GoogleAuthProvider();\n await signInWithPopup(auth, provider);\n toast({\n title: \"Signing in with Google...\",\n description: \"Please follow the instructions in the popup.\",\n });\n } catch (error: any) {\n toast({\n variant: \"destructive\",\n title: \"Google Sign-In Error\",\n description: error.message,\n });\n }\n }\n\n if (isUserLoading || user) {\n return (\n <div className=\"flex justify-center items-center h-screen\">\n <p>Loading...</p>\n </div>\n );\n }\n\n return (\n <div className=\"flex justify-center items-center min-h-[80vh]\">\n <Card className=\"w-full max-w-md\">\n <CardHeader>\n <CardTitle className=\"text-2xl\">Login</CardTitle>\n </CardHeader>\n <CardContent>\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"you@example.com\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"password\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Password</FormLabel>\n <FormControl>\n <Input\n type=\"password\"\n placeholder=\"苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\" className=\"w-full\">\n Login\n </Button>\n </form>\n </Form>\n\n <div className=\"relative my-6\">\n <div className=\"absolute inset-0 flex items-center\">\n <span className=\"w-full border-t\" />\n </div>\n <div className=\"relative flex justify-center text-xs uppercase\">\n <span className=\"bg-background px-2 text-muted-foreground\">\n Or continue with\n </span>\n </div>\n </div>\n\n <Button variant=\"outline\" className=\"w-full\" onClick={handleGoogleSignIn}>\n <img \n src=\"https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/google.svg\" \n alt=\"Google\" \n className=\"mr-2 h-4 w-4\"\n />\n Google\n </Button>\n\n <div className=\"mt-4 text-center text-sm\">\n Don't have an account?{\" \"}\n <Link href=\"/signup\" className=\"underline\">\n Sign up\n </Link>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\movie\\[id]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\movies\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\offline\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":11,"column":56,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[502,516],"text":"You're Offline"},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[502,516],"text":"You‘re Offline"},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[502,516],"text":"You're Offline"},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[502,516],"text":"You’re Offline"},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":13,"column":34,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[586,728],"text":"\r\n It looks like you've lost your internet connection. Some features may be unavailable until you're back online.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[586,728],"text":"\r\n It looks like you‘ve lost your internet connection. Some features may be unavailable until you're back online.\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[586,728],"text":"\r\n It looks like you've lost your internet connection. Some features may be unavailable until you're back online.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[586,728],"text":"\r\n It looks like you’ve lost your internet connection. Some features may be unavailable until you're back online.\r\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":13,"column":111,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[586,728],"text":"\r\n It looks like you've lost your internet connection. Some features may be unavailable until you're back online.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[586,728],"text":"\r\n It looks like you've lost your internet connection. Some features may be unavailable until you‘re back online.\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[586,728],"text":"\r\n It looks like you've lost your internet connection. Some features may be unavailable until you're back online.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[586,728],"text":"\r\n It looks like you've lost your internet connection. Some features may be unavailable until you’re back online.\r\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Link from 'next/link';\r\nimport { WifiOff, Home, ArrowLeft } from 'lucide-react';\r\nimport { Button } from '@/components/ui/button';\r\n\r\nexport default function OfflinePage() {\r\n return (\r\n <div className=\"min-h-[60vh] flex flex-col items-center justify-center px-4 text-center\">\r\n <div className=\"p-4 bg-muted rounded-full mb-6\">\r\n <WifiOff className=\"h-12 w-12 text-muted-foreground\" />\r\n </div>\r\n <h1 className=\"text-3xl font-bold mb-4\">You're Offline</h1>\r\n <p className=\"text-muted-foreground max-w-md mb-8\">\r\n It looks like you've lost your internet connection. Some features may be unavailable until you're back online.\r\n </p>\r\n <div className=\"flex gap-4\">\r\n <Button asChild variant=\"outline\">\r\n <Link href=\"/\">\r\n <Home className=\"mr-2 h-4 w-4\" />\r\n Go Home\r\n </Link>\r\n </Button>\r\n <Button onClick={() => window.history.back()}>\r\n <ArrowLeft className=\"mr-2 h-4 w-4\" />\r\n Go Back\r\n </Button>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\person\\[id]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\privacy\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":28,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse ("we,\" \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (“we,\" \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse ("we,\" \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (”we,\" \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":32,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we," \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,“ \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we," \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,” \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":34,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" "us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" “us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" "us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" ”us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":38,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us," \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,“ \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us," \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,” \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":40,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" "our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" “our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" "our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" ”our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":45,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our," or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,“ or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our," or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,” or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":50,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or "Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or “Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or "Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or ”Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":19,"column":58,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or \"Company") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or \"Company“) is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or \"Company") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[796,1050],"text":"\n StreamVerse (\"we,\" \"us,\" \"our,\" or \"Company”) is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":70,"column":128,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[3561,3736],"text":"\n We use Firebase (provided by Google) for authentication and data storage. Your information is subject to Firebase's privacy practices. We also use:\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[3561,3736],"text":"\n We use Firebase (provided by Google) for authentication and data storage. Your information is subject to Firebase‘s privacy practices. We also use:\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[3561,3736],"text":"\n We use Firebase (provided by Google) for authentication and data storage. Your information is subject to Firebase's privacy practices. We also use:\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[3561,3736],"text":"\n We use Firebase (provided by Google) for authentication and data storage. Your information is subject to Firebase’s privacy practices. We also use:\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":105,"column":80,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5445,5466],"text":"8. Children's Privacy"},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[5445,5466],"text":"8. Children‘s Privacy"},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[5445,5466],"text":"8. Children's Privacy"},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[5445,5466],"text":"8. Children’s Privacy"},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":121,"column":166,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last updated\" date.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the “Last updated\" date.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last updated\" date.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the ”Last updated\" date.\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":121,"column":179,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the \"Last updated" date.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the \"Last updated“ date.\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the \"Last updated" date.\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[6426,6625],"text":"\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the \"Last updated” date.\n "},"desc":"Replace with `”`."}]}],"suppressedMessages":[],"errorCount":12,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export default function PrivacyPolicy() {\n return (\n <div className=\"min-h-screen bg-gradient-to-b from-background to-background/80\">\n {/* Header */}\n <div className=\"bg-gradient-to-r from-primary/20 to-primary/5 py-12 border-b\">\n <div className=\"container mx-auto px-4\">\n <h1 className=\"text-4xl font-bold mb-2\">Privacy Policy</h1>\n <p className=\"text-muted-foreground\">Last updated: January 2025</p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-4 py-12 max-w-4xl\">\n <div className=\"prose prose-invert max-w-none space-y-8\">\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">1. Introduction</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse (\"we,\" \"us,\" \"our,\" or \"Company\") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website and use our services.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">2. Information We Collect</h2>\n <div className=\"space-y-4\">\n <div>\n <h3 className=\"text-lg font-semibold mb-2 text-foreground\">Personal Information</h3>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Email address</li>\n <li>Password (encrypted)</li>\n <li>Account preferences</li>\n <li>Profile information (optional)</li>\n </ul>\n </div>\n <div>\n <h3 className=\"text-lg font-semibold mb-2 text-foreground\">Usage Data</h3>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Pages visited and time spent</li>\n <li>Content watched and ratings</li>\n <li>Device information</li>\n <li>Browser type and IP address</li>\n </ul>\n </div>\n </div>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">3. How We Use Your Information</h2>\n <p className=\"text-muted-foreground mb-3\">We use the information we collect to:</p>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Provide, operate, and maintain our services</li>\n <li>Improve and personalize your experience</li>\n <li>Communicate with you about updates and services</li>\n <li>Process your transactions and send related information</li>\n <li>Comply with legal obligations</li>\n <li>Monitor and analyze trends and usage</li>\n </ul>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">4. Data Security</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n We implement appropriate technical and organizational measures to protect your personal information. However, no method of transmission over the internet is 100% secure. We cannot guarantee absolute security of your personal information.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">5. Firebase and Third-Party Services</h2>\n <p className=\"text-muted-foreground mb-3\">\n We use Firebase (provided by Google) for authentication and data storage. Your information is subject to Firebase's privacy practices. We also use:\n </p>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>The Movie Database (TMDB) API for content information</li>\n <li>Analytics tools to understand usage patterns</li>\n <li>Third-party service providers as needed</li>\n </ul>\n <p className=\"text-muted-foreground mt-3\">\n Please review their privacy policies to understand how they handle your data.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">6. Your Rights</h2>\n <p className=\"text-muted-foreground mb-3\">You have the right to:</p>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Access your personal data</li>\n <li>Correct inaccurate data</li>\n <li>Request deletion of your data</li>\n <li>Opt-out of certain data collection</li>\n <li>Data portability</li>\n </ul>\n <p className=\"text-muted-foreground mt-3\">\n To exercise these rights, please contact us at support@streamverse.com\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">7. Cookies and Tracking</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n We use cookies to enhance your experience. You can control cookie preferences through your browser settings. Some features may not work properly if you disable cookies.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">8. Children's Privacy</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse is not intended for children under 13. We do not knowingly collect personal information from children under 13. If we become aware of such collection, we will delete such information immediately.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">9. International Data Transfers</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n Your information may be transferred to, stored in, and processed in countries other than your country of residence. These countries may have data protection laws that differ from your home country.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">10. Changes to This Policy</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n We may update this Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the \"Last updated\" date.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">11. Contact Us</h2>\n <p className=\"text-muted-foreground leading-relaxed mb-3\">\n If you have questions about this Privacy Policy, please contact us at:\n </p>\n <div className=\"bg-muted rounded-lg p-6 border border-border\">\n <p className=\"font-semibold text-foreground\">StreamVerse Support</p>\n <p className=\"text-muted-foreground mt-2\">Email: support@streamverse.com</p>\n <p className=\"text-muted-foreground\">Los Angeles, CA 90001</p>\n <p className=\"text-muted-foreground\">United States</p>\n </div>\n </section>\n\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\search\\advanced\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":59,"column":67,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2458,2516],"text":"Find exactly what you're looking for with advanced filters"},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[2458,2516],"text":"Find exactly what you‘re looking for with advanced filters"},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2458,2516],"text":"Find exactly what you're looking for with advanced filters"},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[2458,2516],"text":"Find exactly what you’re looking for with advanced filters"},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":82,"column":88,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[3451,3457],"text":" for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[3451,3457],"text":" for “"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[3451,3457],"text":" for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[3451,3457],"text":" for ”"},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":82,"column":112,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[3480,3501],"text":""\r\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[3480,3501],"text":"“\r\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[3480,3501],"text":""\r\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[3480,3501],"text":"”\r\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":94,"column":97,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[4017,4076],"text":"Try adjusting your filters to find what you're looking for."},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[4017,4076],"text":"Try adjusting your filters to find what you‘re looking for."},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[4017,4076],"text":"Try adjusting your filters to find what you're looking for."},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[4017,4076],"text":"Try adjusting your filters to find what you’re looking for."},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useState, useCallback } from 'react';\r\nimport { useRouter, useSearchParams } from 'next/navigation';\r\nimport { AdvancedSearchFilters, type SearchFilters } from '@/components/search/advanced-search-filters';\r\nimport { ContentCard } from '@/components/content/content-card';\r\nimport { advancedSearchCached } from '@/lib/tmdb-cached';\r\nimport type { Content } from '@/lib/types';\r\nimport { Loader2 } from 'lucide-react';\r\n\r\nexport default function AdvancedSearchPage() {\r\n const router = useRouter();\r\n const searchParams = useSearchParams();\r\n const [results, setResults] = useState<Content[]>([]);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [hasSearched, setHasSearched] = useState(false);\r\n const [currentFilters, setCurrentFilters] = useState<SearchFilters | null>(null);\r\n\r\n const handleSearch = useCallback(async (filters: SearchFilters) => {\r\n setIsLoading(true);\r\n setCurrentFilters(filters);\r\n setHasSearched(true);\r\n\r\n try {\r\n const searchResults = await advancedSearchCached({\r\n query: filters.query,\r\n type: filters.type,\r\n year: filters.year,\r\n minRating: filters.minRating,\r\n maxRating: filters.maxRating,\r\n sortBy: filters.sortBy,\r\n genreIds: filters.genreIds,\r\n language: filters.language,\r\n });\r\n setResults(searchResults);\r\n const params = new URLSearchParams();\r\n params.set('q', filters.query);\r\n if (filters.type !== 'all') params.set('type', filters.type);\r\n if (filters.year) params.set('year', filters.year.toString());\r\n if (filters.minRating > 0) params.set('minRating', filters.minRating.toString());\r\n if (filters.maxRating < 10) params.set('maxRating', filters.maxRating.toString());\r\n if (filters.sortBy !== 'popularity.desc') params.set('sortBy', filters.sortBy);\r\n if (filters.genreIds.length > 0) params.set('genres', filters.genreIds.join(','));\r\n if (filters.language) params.set('language', filters.language);\r\n\r\n router.push(`/search/advanced?${params.toString()}`);\r\n } catch (error) {\r\n console.error('Search failed:', error);\r\n setResults([]);\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, [router]);\r\n\r\n return (\r\n <div className=\"container mx-auto px-4 py-8\">\r\n <div className=\"mb-8\">\r\n <h1 className=\"text-4xl font-bold mb-2\">Advanced Search</h1>\r\n <p className=\"text-muted-foreground\">Find exactly what you're looking for with advanced filters</p>\r\n </div>\r\n\r\n <div className=\"grid grid-cols-1 lg:grid-cols-4 gap-6\">\r\n {/* Filters Sidebar */}\r\n <div className=\"lg:col-span-1\">\r\n <AdvancedSearchFilters onSearch={handleSearch} isLoading={isLoading} />\r\n </div>\r\n\r\n {/* Results Area */}\r\n <div className=\"lg:col-span-3\">\r\n {isLoading ? (\r\n <div className=\"flex items-center justify-center py-16\">\r\n <div className=\"text-center\">\r\n <Loader2 className=\"w-8 h-8 animate-spin mx-auto mb-2\" />\r\n <p className=\"text-muted-foreground\">Searching...</p>\r\n </div>\r\n </div>\r\n ) : hasSearched ? (\r\n results.length > 0 ? (\r\n <div>\r\n <div className=\"mb-4\">\r\n <p className=\"text-sm text-muted-foreground\">\r\n Found {results.length} result{results.length !== 1 ? 's' : ''} for \"{currentFilters?.query}\"\r\n </p>\r\n </div>\r\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-4\">\r\n {results.map((item) => (\r\n <ContentCard key={`${item.type}-${item.id}`} item={item} />\r\n ))}\r\n </div>\r\n </div>\r\n ) : (\r\n <div className=\"text-center py-16 border rounded-lg\">\r\n <p className=\"text-lg font-medium mb-2\">No results found</p>\r\n <p className=\"text-muted-foreground\">Try adjusting your filters to find what you're looking for.</p>\r\n </div>\r\n )\r\n ) : (\r\n <div className=\"text-center py-16 border rounded-lg\">\r\n <p className=\"text-lg text-muted-foreground\">Use the filters on the left to search</p>\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\search\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":27,"column":83,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[993,1015],"text":"No results found for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[993,1015],"text":"No results found for “"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[993,1015],"text":"No results found for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[993,1015],"text":"No results found for ”"},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":27,"column":91,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[1022,1024],"text":""."},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[1022,1024],"text":"“."},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[1022,1024],"text":""."},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[1022,1024],"text":"”."},"desc":"Replace with `”`."}]}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { searchContentCached as searchContent } from \"@/lib/tmdb-cached\";\nimport { ContentCard } from \"@/components/content/content-card\";\n\ntype SearchPageProps = {\n searchParams: Promise<{ q?: string }>;\n};\n\nexport default async function SearchPage({ searchParams }: SearchPageProps) {\n const params = await searchParams;\n const query = params.q || \"\";\n const results = await searchContent(query);\n\n return (\n <div className=\"container mx-auto px-4 py-8\">\n <h1 className=\"text-3xl font-bold mb-8\">\n {query ? `Results for \"${query}\"` : \"Search\"}\n </h1>\n {results.length > 0 ? (\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4\">\n {results.map((item) => (\n <ContentCard key={`${item.type}-${item.id}`} item={item} />\n ))}\n </div>\n ) : (\n query && (\n <div className=\"text-center py-16\">\n <p className=\"text-muted-foreground text-lg\">No results found for \"{query}\".</p>\n <p className=\"text-muted-foreground mt-2\">Try a different search term.</p>\n </div>\n )\n )}\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\series\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\signup\\page.tsx","messages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":179,"column":13,"nodeType":"JSXOpeningElement","endLine":183,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\n\"use client\";\n\nimport { useForm } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport * as z from \"zod\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport Link from \"next/link\";\nimport { useAuth } from \"@/firebase\";\nimport { useRouter } from \"next/navigation\";\nimport { useToast } from \"@/hooks/use-toast\";\nimport { useEffect } from \"react\";\nimport { useUser } from \"@/firebase/provider\";\nimport { createUserWithEmailAndPassword, signInWithPopup, GoogleAuthProvider } from \"firebase/auth\";\n\nconst formSchema = z\n .object({\n email: z.string().email({ message: \"Please enter a valid email.\" }),\n password: z\n .string()\n .min(6, { message: \"Password must be at least 6 characters.\" }),\n confirmPassword: z.string(),\n })\n .refine((data) => data.password === data.confirmPassword, {\n message: \"Passwords do not match.\",\n path: [\"confirmPassword\"],\n });\n\nexport default function SignupPage() {\n const auth = useAuth();\n const router = useRouter();\n const { toast } = useToast();\n const { user, isUserLoading } = useUser();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n email: \"\",\n password: \"\",\n confirmPassword: \"\",\n },\n });\n\n useEffect(() => {\n if (!isUserLoading && user) {\n router.push(\"/\");\n }\n }, [user, isUserLoading, router]);\n\n const onSubmit = async (values: z.infer<typeof formSchema>) => {\n try {\n await createUserWithEmailAndPassword(auth, values.email, values.password);\n toast({\n title: \"Account created!\",\n description: \"You are now being signed in.\",\n });\n } catch (error: any) {\n toast({\n variant: \"destructive\",\n title: \"Sign-up Error\",\n description: error.message,\n });\n }\n };\n\n const handleGoogleSignIn = async () => {\n try {\n const provider = new GoogleAuthProvider();\n await signInWithPopup(auth, provider);\n toast({\n title: \"Signing in with Google...\",\n description: \"Please follow the instructions in the popup.\",\n });\n } catch (error: any) {\n toast({\n variant: \"destructive\",\n title: \"Google Sign-In Error\",\n description: error.message,\n });\n }\n }\n\n if (isUserLoading || user) {\n return (\n <div className=\"flex justify-center items-center h-screen\">\n <p>Loading...</p>\n </div>\n );\n }\n\n return (\n <div className=\"flex justify-center items-center min-h-[80vh]\">\n <Card className=\"w-full max-w-md\">\n <CardHeader>\n <CardTitle className=\"text-2xl\">Create an account</CardTitle>\n </CardHeader>\n <CardContent>\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email</FormLabel>\n <FormControl>\n <Input\n type=\"email\"\n placeholder=\"you@example.com\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"password\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Password</FormLabel>\n <FormControl>\n <Input\n type=\"password\"\n placeholder=\"苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"confirmPassword\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Confirm Password</FormLabel>\n <FormControl>\n <Input\n type=\"password\"\n placeholder=\"苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\" className=\"w-full\">\n Create Account\n </Button>\n </form>\n </Form>\n \n <div className=\"relative my-6\">\n <div className=\"absolute inset-0 flex items-center\">\n <span className=\"w-full border-t\" />\n </div>\n <div className=\"relative flex justify-center text-xs uppercase\">\n <span className=\"bg-background px-2 text-muted-foreground\">\n Or continue with\n </span>\n </div>\n </div>\n\n <Button variant=\"outline\" className=\"w-full\" onClick={handleGoogleSignIn}>\n <img \n src=\"https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/google.svg\" \n alt=\"Google\" \n className=\"mr-2 h-4 w-4\"\n />\n Google\n </Button>\n\n <div className=\"mt-4 text-center text-sm\">\n Already have an account?{\" \"}\n <Link href=\"/login\" className=\"underline\">\n Login\n </Link>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\terms\\page.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":33,"column":67,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or "mirroring\" the materials on any other server"},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or “mirroring\" the materials on any other server"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or "mirroring\" the materials on any other server"},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or ”mirroring\" the materials on any other server"},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":33,"column":77,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or \"mirroring" the materials on any other server"},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or \"mirroring“ the materials on any other server"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or \"mirroring" the materials on any other server"},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[1959,2052],"text":"Transferring the materials to another person or \"mirroring” the materials on any other server"},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":41,"column":63,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an 'as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an ‘as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an 'as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an ’as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":41,"column":69,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an 'as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an 'as is‘ basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an 'as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[2339,2731],"text":"\n The materials on StreamVerse are provided on an 'as is’ basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export default function TermsOfService() {\n return (\n <div className=\"min-h-screen bg-gradient-to-b from-background to-background/80\">\n {/* Header */}\n <div className=\"bg-gradient-to-r from-primary/20 to-primary/5 py-12 border-b\">\n <div className=\"container mx-auto px-4\">\n <h1 className=\"text-4xl font-bold mb-2\">Terms of Service</h1>\n <p className=\"text-muted-foreground\">Last updated: January 2025</p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"container mx-auto px-4 py-12 max-w-4xl\">\n <div className=\"prose prose-invert max-w-none space-y-8\">\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">1. Agreement to Terms</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n By accessing and using StreamVerse, you accept and agree to be bound by the terms and provision of this agreement. If you do not agree to abide by the above, please do not use this service.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">2. Use License</h2>\n <p className=\"text-muted-foreground mb-3\">\n Permission is granted to temporarily download one copy of the materials (information or software) on StreamVerse for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not:\n </p>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Modifying or copying the materials</li>\n <li>Using the materials for any commercial purpose or for any public display</li>\n <li>Attempting to decompile or reverse engineer any software contained on StreamVerse</li>\n <li>Removing any copyright or other proprietary notations from the materials</li>\n <li>Transferring the materials to another person or \"mirroring\" the materials on any other server</li>\n <li>Creating competing services using our content</li>\n </ul>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">3. Disclaimer</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n The materials on StreamVerse are provided on an 'as is' basis. StreamVerse makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">4. Limitations</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n In no event shall StreamVerse or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on StreamVerse, even if StreamVerse or an authorized representative has been notified orally or in writing of the possibility of such damage.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">5. Accuracy of Materials</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n The materials appearing on StreamVerse could include technical, typographical, or photographic errors. StreamVerse does not warrant that any of the materials on its website are accurate, complete, or current. StreamVerse may make changes to the materials contained on its website at any time without notice.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">6. Content Ownership</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse does not own or create the entertainment content displayed. All movie and TV show information is sourced from The Movie Database (TMDB). We are not responsible for the accuracy or availability of third-party content. Copyright in and to the content on StreamVerse is owned by the respective content creators and distributors.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">7. User Accounts</h2>\n <p className=\"text-muted-foreground mb-3\">\n When you create an account with StreamVerse, you are responsible for:\n </p>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Maintaining the confidentiality of your password</li>\n <li>Accepting responsibility for all activities that occur under your account</li>\n <li>Notifying us immediately of any unauthorized use of your account</li>\n <li>Providing accurate and complete information during registration</li>\n </ul>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">8. Prohibited Conduct</h2>\n <p className=\"text-muted-foreground mb-3\">\n You agree not to:\n </p>\n <ul className=\"list-disc list-inside text-muted-foreground space-y-2\">\n <li>Violate any applicable laws or regulations</li>\n <li>Infringe upon the intellectual property rights of others</li>\n <li>Harass, abuse, or threaten other users</li>\n <li>Post spam, viruses, or malicious code</li>\n <li>Attempt to gain unauthorized access to our systems</li>\n <li>Use automated tools or bots to scrape our content</li>\n <li>Engage in any fraudulent or deceptive activity</li>\n </ul>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">9. Termination</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse may terminate or suspend your account and access immediately, without prior notice or liability, for any reason whatsoever, including if you breach the Terms.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">10. Modifications to Service</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse reserves the right to modify or discontinue, temporarily or permanently, the service (or any part thereof) with or without notice. You agree that StreamVerse shall not be liable to you or to any third party for any modification, suspension or discontinuance of the service.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">11. External Links</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse is not responsible for any external links or third-party websites. Your use of external links and third-party websites is at your own risk and subject to the terms and conditions of those sites.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">12. Governing Law</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n These terms and conditions are governed by and construed in accordance with the laws of the United States, and you irrevocably submit to the exclusive jurisdiction of the courts in that location.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">13. Changes to Terms</h2>\n <p className=\"text-muted-foreground leading-relaxed\">\n StreamVerse reserves the right to revise these terms at any time. By using this website, you are agreeing to be bound by the then current version of these terms of use.\n </p>\n </section>\n\n <section>\n <h2 className=\"text-2xl font-bold mb-4 text-foreground\">14. Contact Information</h2>\n <p className=\"text-muted-foreground mb-3\">\n If you have any questions about these Terms of Service, please contact us:\n </p>\n <div className=\"bg-muted rounded-lg p-6 border border-border\">\n <p className=\"font-semibold text-foreground\">StreamVerse Support</p>\n <p className=\"text-muted-foreground mt-2\">Email: support@streamverse.com</p>\n <p className=\"text-muted-foreground\">Phone: +1 (555) 123-4567</p>\n <p className=\"text-muted-foreground\">Los Angeles, CA 90001</p>\n </div>\n </section>\n\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\tv\\[id]\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\app\\watchlist\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\FirebaseErrorListener.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\add-content-dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\confirmation-dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\content-details-dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\delete-progress-dialog.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\components\\admin\\delete-progress-dialog.tsx:46:7\n 44 | useEffect(() => {\n 45 | if (!open) {\n> 46 | setProgress(0);\n | ^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 47 | return;\n 48 | }\n 49 |","line":46,"column":7,"nodeType":null,"endLine":46,"endColumn":18}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect, useState } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@/components/ui/dialog';\nimport { Progress } from '@/components/ui/progress';\nimport { Film, Tv, User, Search, CheckCircle2, Loader2 } from 'lucide-react';\nimport { cn } from '@/lib/utils';\n\nexport interface DeleteTask {\n id: string;\n label: string;\n type: 'movie' | 'tv' | 'person' | 'search';\n status: 'pending' | 'deleting' | 'completed';\n total: number;\n current: number;\n}\n\ninterface DeleteProgressDialogProps {\n open: boolean;\n tasks: DeleteTask[];\n onComplete?: () => void;\n}\n\nconst iconMap = {\n movie: Film,\n tv: Tv,\n person: User,\n search: Search,\n};\n\nexport function DeleteProgressDialog({\n open,\n tasks,\n onComplete,\n}: DeleteProgressDialogProps) {\n const [progress, setProgress] = useState(0);\n\n useEffect(() => {\n if (!open) {\n setProgress(0);\n return;\n }\n\n const totalDocuments = tasks.reduce((sum, t) => sum + t.total, 0);\n const deletedDocuments = tasks.reduce((sum, t) => sum + t.current, 0);\n const newProgress = totalDocuments > 0 ? (deletedDocuments / totalDocuments) * 100 : 0;\n\n setProgress(newProgress);\n\n if (newProgress === 100 && onComplete) {\n setTimeout(() => {\n onComplete();\n }, 500);\n }\n }, [tasks, open, onComplete]);\n\n const totalDocuments = tasks.reduce((sum, t) => sum + t.total, 0);\n const deletedDocuments = tasks.reduce((sum, t) => sum + t.current, 0);\n\n return (\n <Dialog open={open}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>Deleting Cache</DialogTitle>\n <DialogDescription>\n Removing cached data from database...\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-muted-foreground\">Overall Progress</span>\n <span className=\"font-medium\">\n {deletedDocuments} / {totalDocuments}\n </span>\n </div>\n <Progress value={progress} className=\"h-2\" />\n <div className=\"text-center text-sm font-medium\">\n {Math.round(progress)}%\n </div>\n </div>\n\n <div className=\"space-y-3 max-h-96\">\n {tasks.map((task) => {\n const Icon = iconMap[task.type];\n const isDeleting = task.status === 'deleting';\n const isCompleted = task.status === 'completed';\n const taskProgress = task.total > 0 ? (task.current / task.total) * 100 : 0;\n\n return (\n <div\n key={task.id}\n className={cn(\n 'rounded-lg border p-3 transition-all',\n isCompleted && 'bg-green-500/10 border-green-500/20',\n isDeleting && 'bg-blue-500/10 border-blue-500/20',\n !isDeleting && !isCompleted && 'bg-muted/50'\n )}\n >\n <div className=\"flex items-start gap-3 mb-2\">\n <Icon\n className={cn(\n 'h-5 w-5 flex-shrink-0 mt-0.5',\n isCompleted && 'text-green-500',\n isDeleting && 'text-blue-500',\n !isDeleting && !isCompleted && 'text-muted-foreground'\n )}\n />\n <div className=\"flex-1 min-w-0\">\n <p\n className={cn(\n 'text-sm font-medium',\n isCompleted && 'text-green-600 dark:text-green-400',\n isDeleting && 'text-blue-600 dark:text-blue-400'\n )}\n >\n {task.label}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {task.current} / {task.total} items\n </p>\n </div>\n {isCompleted && (\n <CheckCircle2 className=\"h-5 w-5 text-green-500 flex-shrink-0\" />\n )}\n </div>\n\n {!isCompleted && (\n <div className=\"ml-8 space-y-1\">\n <Progress value={taskProgress} className=\"h-1.5\" />\n <p className=\"text-xs text-muted-foreground text-right\">\n {Math.round(taskProgress)}%\n </p>\n </div>\n )}\n </div>\n );\n })}\n </div>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\settings\\configuration-tab.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'firestore'. Either include it or remove the dependency array.","line":133,"column":6,"nodeType":"ArrayExpression","endLine":133,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [firestore]","fix":{"range":[3664,3666],"text":"[firestore]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState } from 'react';\r\nimport { doc, getDoc, setDoc } from 'firebase/firestore';\r\nimport { useFirebase } from '@/firebase';\r\nimport { clearSettingsCache } from '@/lib/system-settings';\r\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\r\nimport { Button } from '@/components/ui/button';\r\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';\r\nimport { AlertCircle, Loader2, CheckCircle, Eye, EyeOff } from 'lucide-react';\r\n\r\ninterface ProjectSettings {\r\n ai: {\r\n geminiApiKey: string;\r\n geminiModel: '2.5-pro' | '2.5-flash' | '2.5-flash-lite';\r\n recommendationsCaching: boolean;\r\n recommendationsLimit: number;\r\n };\r\n tmdb: {\r\n apiKey: string;\r\n baseUrl: string;\r\n };\r\n branding: {\r\n siteName: string;\r\n tagline: string;\r\n primaryColor: string;\r\n supportEmail: string;\r\n };\r\n notifications: {\r\n enableEmailNotifications: boolean;\r\n notificationRecipient: string;\r\n digestFrequency: 'daily' | 'weekly' | 'monthly';\r\n };\r\n contentFiltering: {\r\n minRating: number;\r\n ageFilter: '0' | '13' | '16' | '18';\r\n blockedGenres: string[];\r\n restrictedRegions: string[];\r\n };\r\n userSettings: {\r\n sessionTimeout: number;\r\n maxLoginAttempts: number;\r\n passwordExpirationDays: number;\r\n emailVerificationRequired: boolean;\r\n };\r\n rateLimiting: {\r\n apiRateLimit: number;\r\n searchRateLimit: number;\r\n };\r\n analytics: {\r\n firebaseTrackingEnabled: boolean;\r\n firebaseTrackingId: string;\r\n dataRetentionDays: number;\r\n };\r\n}\r\n\r\nconst DEFAULT_SETTINGS: ProjectSettings = {\r\n ai: {\r\n geminiApiKey: '',\r\n geminiModel: '2.5-flash',\r\n recommendationsCaching: true,\r\n recommendationsLimit: 5,\r\n },\r\n tmdb: {\r\n apiKey: '',\r\n baseUrl: 'https://api.themoviedb.org/3',\r\n },\r\n branding: {\r\n siteName: 'StreamVerse',\r\n tagline: 'Your Streaming Universe',\r\n primaryColor: '#3b82f6',\r\n supportEmail: 'support@streamverse.com',\r\n },\r\n notifications: {\r\n enableEmailNotifications: true,\r\n notificationRecipient: '',\r\n digestFrequency: 'weekly',\r\n },\r\n contentFiltering: {\r\n minRating: 0,\r\n ageFilter: '0',\r\n blockedGenres: [],\r\n restrictedRegions: [],\r\n },\r\n userSettings: {\r\n sessionTimeout: 3600,\r\n maxLoginAttempts: 5,\r\n passwordExpirationDays: 90,\r\n emailVerificationRequired: true,\r\n },\r\n rateLimiting: {\r\n apiRateLimit: 1000,\r\n searchRateLimit: 30,\r\n },\r\n analytics: {\r\n firebaseTrackingEnabled: true,\r\n firebaseTrackingId: '',\r\n dataRetentionDays: 90,\r\n },\r\n};\r\n\r\nexport default function ConfigurationTab() {\r\n const { firestore } = useFirebase();\r\n const [settings, setSettings] = useState<ProjectSettings>(DEFAULT_SETTINGS);\r\n const [isLoading, setIsLoading] = useState(true);\r\n const [isSaving, setIsSaving] = useState(false);\r\n const [saved, setSaved] = useState(false);\r\n const [error, setError] = useState('');\r\n const [showPasswords, setShowPasswords] = useState({\r\n geminiApiKey: false,\r\n tmdbApiKey: false,\r\n });\r\n\r\n useEffect(() => {\r\n const fetchSettings = async () => {\r\n try {\r\n const db = firestore;\r\n const docRef = doc(db, 'system', 'projectSettings');\r\n const docSnap = await getDoc(docRef);\r\n\r\n if (docSnap.exists()) {\r\n setSettings({ ...DEFAULT_SETTINGS, ...docSnap.data() } as ProjectSettings);\r\n }\r\n } catch (err) {\r\n console.error('Error fetching settings:', err);\r\n setError('Failed to load settings');\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n };\r\n\r\n fetchSettings();\r\n }, []);\r\n\r\n const handleSave = async () => {\r\n if (!settings.ai.geminiApiKey) {\r\n setError('Gemini API key is required');\r\n return;\r\n }\r\n if (!settings.tmdb.apiKey) {\r\n setError('TMDB API key is required');\r\n return;\r\n }\r\n if (!settings.branding.siteName || !settings.branding.supportEmail) {\r\n setError('Site name and support email are required');\r\n return;\r\n }\r\n\r\n setIsSaving(true);\r\n setError('');\r\n try {\r\n const db = firestore;\r\n const docRef = doc(db, 'system', 'projectSettings');\r\n await setDoc(docRef, settings);\r\n\r\n clearSettingsCache();\r\n\r\n setSaved(true);\r\n setTimeout(() => setSaved(false), 3000);\r\n } catch (err) {\r\n console.error('Error saving settings:', err);\r\n setError('Failed to save settings');\r\n } finally {\r\n setIsSaving(false);\r\n }\r\n };\r\n\r\n const updateSetting = (path: string, value: any) => {\r\n setSettings(prev => {\r\n const keys = path.split('.');\r\n const newSettings = JSON.parse(JSON.stringify(prev));\r\n let current = newSettings;\r\n for (let i = 0; i < keys.length - 1; i++) {\r\n current = current[keys[i]];\r\n }\r\n current[keys[keys.length - 1]] = value;\r\n return newSettings;\r\n });\r\n };\r\n\r\n if (isLoading) {\r\n return (\r\n <div className=\"flex justify-center py-12\">\r\n <Loader2 className=\"h-8 w-8 animate-spin\" />\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"space-y-6\">\r\n <div>\r\n <h1 className=\"text-4xl font-bold mb-2\">System Settings</h1>\r\n <p className=\"text-muted-foreground\">Configure all system-wide settings and integrations</p>\r\n </div>\r\n\r\n {error && (\r\n <div className=\"bg-red-500/10 border border-red-500/50 rounded-lg p-4\">\r\n <p className=\"text-red-500 text-sm flex items-center gap-2\">\r\n <AlertCircle className=\"h-4 w-4\" />\r\n {error}\r\n </p>\r\n </div>\r\n )}\r\n\r\n <Card className=\"border border-border\">\r\n <CardHeader>\r\n <CardTitle>Configuration</CardTitle>\r\n </CardHeader>\r\n <CardContent>\r\n <Tabs defaultValue=\"ai\" className=\"space-y-6\">\r\n <TabsList className=\"grid w-full grid-cols-4 lg:grid-cols-8 gap-2\">\r\n <TabsTrigger value=\"ai\">AI</TabsTrigger>\r\n <TabsTrigger value=\"tmdb\">TMDB</TabsTrigger>\r\n <TabsTrigger value=\"branding\">Branding</TabsTrigger>\r\n <TabsTrigger value=\"notifications\">Notifications</TabsTrigger>\r\n <TabsTrigger value=\"filtering\">Filtering</TabsTrigger>\r\n <TabsTrigger value=\"users\">Users</TabsTrigger>\r\n <TabsTrigger value=\"rate\">Rate Limit</TabsTrigger>\r\n <TabsTrigger value=\"analytics\">Analytics</TabsTrigger>\r\n </TabsList>\r\n\r\n {/* AI Configuration Tab */}\r\n <TabsContent value=\"ai\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Gemini API Key</label>\r\n <div className=\"relative\">\r\n <input\r\n type={showPasswords.geminiApiKey ? 'text' : 'password'}\r\n value={settings.ai.geminiApiKey}\r\n onChange={(e) => updateSetting('ai.geminiApiKey', e.target.value)}\r\n placeholder=\"Enter your Gemini API key\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground pr-10\"\r\n />\r\n <button\r\n onClick={() => setShowPasswords(p => ({ ...p, geminiApiKey: !p.geminiApiKey }))}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\r\n >\r\n {showPasswords.geminiApiKey ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\r\n </button>\r\n </div>\r\n <p className=\"text-xs text-muted-foreground mt-1\">Get it from Google AI Studio</p>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Model</label>\r\n <select\r\n value={settings.ai.geminiModel}\r\n onChange={(e) => updateSetting('ai.geminiModel', e.target.value)}\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n >\r\n <option value=\"2.5-pro\">Gemini 2.5 Pro</option>\r\n <option value=\"2.5-flash\">Gemini 2.5 Flash</option>\r\n <option value=\"2.5-flash-lite\">Gemini 2.5 Flash Lite</option>\r\n </select>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"rec-caching\"\r\n checked={settings.ai.recommendationsCaching}\r\n onChange={(e) => updateSetting('ai.recommendationsCaching', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"rec-caching\" className=\"text-sm font-medium\">\r\n Enable recommendations caching\r\n </label>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Recommendations Limit</label>\r\n <input\r\n type=\"number\"\r\n value={settings.ai.recommendationsLimit}\r\n onChange={(e) => updateSetting('ai.recommendationsLimit', parseInt(e.target.value))}\r\n min=\"1\"\r\n max=\"50\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* TMDB Configuration Tab */}\r\n <TabsContent value=\"tmdb\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">API Key</label>\r\n <div className=\"relative\">\r\n <input\r\n type={showPasswords.tmdbApiKey ? 'text' : 'password'}\r\n value={settings.tmdb.apiKey}\r\n onChange={(e) => updateSetting('tmdb.apiKey', e.target.value)}\r\n placeholder=\"Enter your TMDB API key\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground pr-10\"\r\n />\r\n <button\r\n onClick={() => setShowPasswords(p => ({ ...p, tmdbApiKey: !p.tmdbApiKey }))}\r\n className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground\"\r\n >\r\n {showPasswords.tmdbApiKey ? <EyeOff className=\"h-4 w-4\" /> : <Eye className=\"h-4 w-4\" />}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Base URL</label>\r\n <input\r\n type=\"text\"\r\n value={settings.tmdb.baseUrl}\r\n onChange={(e) => updateSetting('tmdb.baseUrl', e.target.value)}\r\n placeholder=\"https://api.themoviedb.org/3\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Default: https://api.themoviedb.org/3</p>\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Branding Tab */}\r\n <TabsContent value=\"branding\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Site Name</label>\r\n <input\r\n type=\"text\"\r\n value={settings.branding.siteName}\r\n onChange={(e) => updateSetting('branding.siteName', e.target.value)}\r\n placeholder=\"StreamVerse\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Tagline</label>\r\n <input\r\n type=\"text\"\r\n value={settings.branding.tagline}\r\n onChange={(e) => updateSetting('branding.tagline', e.target.value)}\r\n placeholder=\"Your Streaming Universe\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Primary Color</label>\r\n <div className=\"flex gap-2\">\r\n <input\r\n type=\"color\"\r\n value={settings.branding.primaryColor}\r\n onChange={(e) => updateSetting('branding.primaryColor', e.target.value)}\r\n className=\"h-10 w-20 rounded-lg border border-border cursor-pointer\"\r\n />\r\n <input\r\n type=\"text\"\r\n value={settings.branding.primaryColor}\r\n onChange={(e) => updateSetting('branding.primaryColor', e.target.value)}\r\n placeholder=\"#3b82f6\"\r\n className=\"flex-1 px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Support Email</label>\r\n <input\r\n type=\"email\"\r\n value={settings.branding.supportEmail}\r\n onChange={(e) => updateSetting('branding.supportEmail', e.target.value)}\r\n placeholder=\"support@streamverse.com\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Notifications Tab */}\r\n <TabsContent value=\"notifications\" className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"enable-notifications\"\r\n checked={settings.notifications.enableEmailNotifications}\r\n onChange={(e) => updateSetting('notifications.enableEmailNotifications', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"enable-notifications\" className=\"text-sm font-medium\">\r\n Enable email notifications\r\n </label>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Notification Recipient Email</label>\r\n <input\r\n type=\"email\"\r\n value={settings.notifications.notificationRecipient}\r\n onChange={(e) => updateSetting('notifications.notificationRecipient', e.target.value)}\r\n placeholder=\"notifications@streamverse.com\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Digest Frequency</label>\r\n <select\r\n value={settings.notifications.digestFrequency}\r\n onChange={(e) => updateSetting('notifications.digestFrequency', e.target.value as any)}\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n >\r\n <option value=\"daily\">Daily</option>\r\n <option value=\"weekly\">Weekly</option>\r\n <option value=\"monthly\">Monthly</option>\r\n </select>\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Content Filtering Tab */}\r\n <TabsContent value=\"filtering\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Minimum Rating</label>\r\n <input\r\n type=\"number\"\r\n value={settings.contentFiltering.minRating}\r\n onChange={(e) => updateSetting('contentFiltering.minRating', parseFloat(e.target.value))}\r\n min=\"0\"\r\n max=\"10\"\r\n step=\"0.5\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Age Filter</label>\r\n <select\r\n value={settings.contentFiltering.ageFilter}\r\n onChange={(e) => updateSetting('contentFiltering.ageFilter', e.target.value as any)}\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n >\r\n <option value=\"0\">All Ages</option>\r\n <option value=\"13\">13+</option>\r\n <option value=\"16\">16+</option>\r\n <option value=\"18\">18+</option>\r\n </select>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Blocked Genres (comma-separated IDs)</label>\r\n <input\r\n type=\"text\"\r\n value={settings.contentFiltering.blockedGenres.join(',')}\r\n onChange={(e) => updateSetting('contentFiltering.blockedGenres', e.target.value.split(',').filter(g => g.trim()))}\r\n placeholder=\"27,53\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Restricted Regions (comma-separated codes)</label>\r\n <input\r\n type=\"text\"\r\n value={settings.contentFiltering.restrictedRegions.join(',')}\r\n onChange={(e) => updateSetting('contentFiltering.restrictedRegions', e.target.value.split(',').filter(r => r.trim()))}\r\n placeholder=\"KP,IR\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* User Settings Tab */}\r\n <TabsContent value=\"users\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Session Timeout (seconds)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.userSettings.sessionTimeout}\r\n onChange={(e) => updateSetting('userSettings.sessionTimeout', parseInt(e.target.value))}\r\n min=\"300\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Current: {Math.round(settings.userSettings.sessionTimeout / 60)} minutes</p>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Max Login Attempts</label>\r\n <input\r\n type=\"number\"\r\n value={settings.userSettings.maxLoginAttempts}\r\n onChange={(e) => updateSetting('userSettings.maxLoginAttempts', parseInt(e.target.value))}\r\n min=\"1\"\r\n max=\"10\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Password Expiration (days)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.userSettings.passwordExpirationDays}\r\n onChange={(e) => updateSetting('userSettings.passwordExpirationDays', parseInt(e.target.value))}\r\n min=\"0\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Set to 0 to disable expiration</p>\r\n </div>\r\n\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"email-verification\"\r\n checked={settings.userSettings.emailVerificationRequired}\r\n onChange={(e) => updateSetting('userSettings.emailVerificationRequired', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"email-verification\" className=\"text-sm font-medium\">\r\n Require email verification\r\n </label>\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Rate Limiting Tab */}\r\n <TabsContent value=\"rate\" className=\"space-y-4\">\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">API Rate Limit (requests/hour per user)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.rateLimiting.apiRateLimit}\r\n onChange={(e) => updateSetting('rateLimiting.apiRateLimit', parseInt(e.target.value))}\r\n min=\"100\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Search Rate Limit (searches/hour per user)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.rateLimiting.searchRateLimit}\r\n onChange={(e) => updateSetting('rateLimiting.searchRateLimit', parseInt(e.target.value))}\r\n min=\"5\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n\r\n {/* Analytics Tab */}\r\n <TabsContent value=\"analytics\" className=\"space-y-4\">\r\n <div className=\"flex items-center gap-2\">\r\n <input\r\n type=\"checkbox\"\r\n id=\"firebase-tracking\"\r\n checked={settings.analytics.firebaseTrackingEnabled}\r\n onChange={(e) => updateSetting('analytics.firebaseTrackingEnabled', e.target.checked)}\r\n className=\"w-4 h-4 rounded border-border\"\r\n />\r\n <label htmlFor=\"firebase-tracking\" className=\"text-sm font-medium\">\r\n Enable Firebase Analytics\r\n </label>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Firebase Tracking ID</label>\r\n <input\r\n type=\"text\"\r\n value={settings.analytics.firebaseTrackingId}\r\n onChange={(e) => updateSetting('analytics.firebaseTrackingId', e.target.value)}\r\n placeholder=\"G-XXXXXXXXXX\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n <p className=\"text-xs text-muted-foreground mt-1\">Found in Firebase Console</p>\r\n </div>\r\n\r\n <div>\r\n <label className=\"block text-sm font-semibold mb-2\">Data Retention (days)</label>\r\n <input\r\n type=\"number\"\r\n value={settings.analytics.dataRetentionDays}\r\n onChange={(e) => updateSetting('analytics.dataRetentionDays', parseInt(e.target.value))}\r\n min=\"1\"\r\n className=\"w-full px-4 py-2 rounded-lg bg-background border border-border focus:outline-none focus:ring-2 focus:ring-primary text-foreground\"\r\n />\r\n </div>\r\n </TabsContent>\r\n </Tabs>\r\n\r\n <div className=\"mt-8 flex gap-2 items-center\">\r\n <Button\r\n onClick={handleSave}\r\n disabled={isSaving}\r\n className=\"gap-2\"\r\n >\r\n {isSaving ? (\r\n <Loader2 className=\"h-4 w-4 animate-spin\" />\r\n ) : (\r\n <CheckCircle className=\"h-4 w-4\" />\r\n )}\r\n Save Settings\r\n </Button>\r\n\r\n {saved && (\r\n <div className=\"flex items-center gap-2 text-green-500 text-sm\">\r\n <CheckCircle className=\"h-4 w-4\" />\r\n Saved successfully!\r\n </div>\r\n )}\r\n </div>\r\n </CardContent>\r\n </Card>\r\n\r\n <div className=\"bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 text-sm text-blue-500\">\r\n <p className=\"font-semibold mb-1\">Security Note</p>\r\n <p>All configuration settings are stored securely in Firestore. API keys are marked as sensitive fields.</p>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\settings\\email-tab.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has missing dependencies: 'firestore' and 'toast'. Either include them or remove the dependency array.","line":59,"column":6,"nodeType":"ArrayExpression","endLine":59,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [firestore, toast]","fix":{"range":[1647,1649],"text":"[firestore, toast]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect, useState } from 'react';\nimport { doc, getDoc, setDoc } from 'firebase/firestore';\nimport { useFirebase } from '@/firebase';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { Input } from '@/components/ui/input';\nimport { Label } from '@/components/ui/label';\nimport { AlertCircle, Loader2, CheckCircle } from 'lucide-react';\nimport { useToast } from '@/hooks/use-toast';\n\ninterface SMTPConfig {\n smtpHost: string;\n smtpPort: number;\n smtpUser: string;\n smtpPassword: string;\n fromEmail: string;\n toEmail: string;\n}\n\nexport default function EmailTab() {\n const { firestore } = useFirebase();\n const { toast } = useToast();\n const [config, setConfig] = useState<SMTPConfig>({\n smtpHost: '',\n smtpPort: 587,\n smtpUser: '',\n smtpPassword: '',\n fromEmail: '',\n toEmail: '',\n });\n const [isLoading, setIsLoading] = useState(true);\n const [isSaving, setIsSaving] = useState(false);\n\n useEffect(() => {\n const fetchConfig = async () => {\n try {\n const db = firestore;\n const docRef = doc(db, 'system', 'smtpConfig');\n const docSnap = await getDoc(docRef);\n\n if (docSnap.exists()) {\n setConfig(docSnap.data() as SMTPConfig);\n }\n } catch (error) {\n console.error('Error fetching SMTP config:', error);\n toast({\n title: 'Error',\n description: 'Failed to load SMTP configuration',\n variant: 'destructive',\n });\n } finally {\n setIsLoading(false);\n }\n };\n\n fetchConfig();\n }, []);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const { name, value } = e.target;\n setConfig(prev => ({\n ...prev,\n [name]: name === 'smtpPort' ? parseInt(value) : value,\n }));\n };\n\n const handleSave = async () => {\n if (!config.smtpHost || !config.smtpUser || !config.smtpPassword || !config.fromEmail || !config.toEmail) {\n toast({\n title: 'Validation Error',\n description: 'Please fill in all required fields',\n variant: 'destructive',\n });\n return;\n }\n\n setIsSaving(true);\n try {\n const db = firestore;\n const docRef = doc(db, 'system', 'smtpConfig');\n await setDoc(docRef, config);\n\n toast({\n title: 'Success',\n description: 'SMTP configuration saved successfully',\n });\n } catch (error) {\n console.error('Error saving SMTP config:', error);\n toast({\n title: 'Error',\n description: 'Failed to save configuration',\n variant: 'destructive',\n });\n } finally {\n setIsSaving(false);\n }\n };\n\n if (isLoading) {\n return (\n <div className=\"flex justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n <Card className=\"border border-border max-w-2xl\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <AlertCircle className=\"h-5 w-5 text-yellow-500\" />\n SMTP Configuration\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <p className=\"text-sm text-muted-foreground\">\n Configure SMTP settings for sending contact form emails. These credentials are securely stored in Firestore.\n </p>\n\n <div className=\"space-y-4\">\n <div>\n <Label htmlFor=\"smtpHost\">SMTP Host *</Label>\n <Input\n id=\"smtpHost\"\n name=\"smtpHost\"\n value={config.smtpHost}\n onChange={handleChange}\n placeholder=\"smtp.gmail.com\"\n />\n </div>\n\n <div>\n <Label htmlFor=\"smtpPort\">SMTP Port *</Label>\n <Input\n id=\"smtpPort\"\n name=\"smtpPort\"\n type=\"number\"\n value={config.smtpPort}\n onChange={handleChange}\n placeholder=\"587\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Usually 587 for TLS or 465 for SSL\n </p>\n </div>\n\n <div>\n <Label htmlFor=\"smtpUser\">SMTP Username/Email *</Label>\n <Input\n id=\"smtpUser\"\n name=\"smtpUser\"\n type=\"email\"\n value={config.smtpUser}\n onChange={handleChange}\n placeholder=\"your-email@gmail.com\"\n />\n </div>\n\n <div>\n <Label htmlFor=\"smtpPassword\">SMTP Password *</Label>\n <Input\n id=\"smtpPassword\"\n name=\"smtpPassword\"\n type=\"password\"\n value={config.smtpPassword}\n onChange={handleChange}\n placeholder=\"苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位苇螒位\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n For Gmail, use an app-specific password\n </p>\n </div>\n\n <div>\n <Label htmlFor=\"fromEmail\">From Email Address *</Label>\n <Input\n id=\"fromEmail\"\n name=\"fromEmail\"\n type=\"email\"\n value={config.fromEmail}\n onChange={handleChange}\n placeholder=\"noreply@streamverse.com\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Email address shown as sender\n </p>\n </div>\n\n <div>\n <Label htmlFor=\"toEmail\">To Email Address (Receive submissions) *</Label>\n <Input\n id=\"toEmail\"\n name=\"toEmail\"\n type=\"email\"\n value={config.toEmail}\n onChange={handleChange}\n placeholder=\"support@streamverse.com\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Where contact form submissions will be sent\n </p>\n </div>\n </div>\n\n <Button onClick={handleSave} disabled={isSaving} className=\"gap-2\">\n {isSaving ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <CheckCircle className=\"h-4 w-4\" />}\n Save Configuration\n </Button>\n\n <div className=\"bg-blue-500/10 border border-blue-500/50 rounded-lg p-4 text-sm text-blue-500\">\n <p className=\"font-semibold mb-1\">Privacy Note</p>\n <p>SMTP credentials are stored securely in Firestore and only used on the server to send emails.</p>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\settings\\system-cache-tab.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'fetchStats'. Either include it or remove the dependency array.","line":77,"column":6,"nodeType":"ArrayExpression","endLine":77,"endColumn":8,"suggestions":[{"desc":"Update the dependencies array to be: [fetchStats]","fix":{"range":[2490,2492],"text":"[fetchStats]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect, useState } from 'react';\nimport { collection, getDocs, writeBatch } from 'firebase/firestore';\nimport { useFirebase } from '@/firebase';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { AlertCircle, Trash2, Loader2, RefreshCw } from 'lucide-react';\nimport { ConfirmationDialog } from '@/components/admin/confirmation-dialog';\nimport { DeleteProgressDialog, DeleteTask } from '@/components/admin/delete-progress-dialog';\nimport { useToast } from '@/hooks/use-toast';\n\ninterface SystemStats {\n totalCachedMovies: number;\n totalCachedShows: number;\n totalCachedPersons: number;\n totalSearchCache: number;\n}\n\nexport default function SystemCacheTab() {\n const { firestore } = useFirebase();\n const { toast } = useToast();\n const [stats, setStats] = useState<SystemStats>({\n totalCachedMovies: 0,\n totalCachedShows: 0,\n totalCachedPersons: 0,\n totalSearchCache: 0,\n });\n const [isLoading, setIsLoading] = useState(true);\n const [clearing, setClearing] = useState<string | null>(null);\n const [confirmDialog, setConfirmDialog] = useState<{ open: boolean; type: string; title: string; description: string }>({\n open: false,\n type: '',\n title: '',\n description: '',\n });\n const [deleteTasks, setDeleteTasks] = useState<DeleteTask[]>([]);\n const [progressDialogOpen, setProgressDialogOpen] = useState(false);\n\n const fetchStats = async () => {\n setIsLoading(true);\n try {\n const db = firestore;\n\n const moviesRef = collection(db, 'movies');\n const moviesSnap = await getDocs(moviesRef);\n\n const showsRef = collection(db, 'tvShows');\n const showsSnap = await getDocs(showsRef);\n\n const personsRef = collection(db, 'persons');\n const personsSnap = await getDocs(personsRef);\n\n const cacheRef = collection(db, 'searchCache');\n const cacheSnap = await getDocs(cacheRef);\n\n setStats({\n totalCachedMovies: moviesSnap.size,\n totalCachedShows: showsSnap.size,\n totalCachedPersons: personsSnap.size,\n totalSearchCache: cacheSnap.size,\n });\n } catch (error) {\n console.error('Error fetching system stats:', error);\n toast({\n title: 'Error',\n description: 'Failed to load cache statistics',\n variant: 'destructive',\n });\n } finally {\n setIsLoading(false);\n }\n };\n\n useEffect(() => {\n fetchStats();\n }, []);\n\n const openConfirmDialog = (type: string) => {\n const labels: Record<string, { title: string; description: string }> = {\n movies: {\n title: 'Clear Movies Cache',\n description: `This will permanently delete ${stats.totalCachedMovies} cached movies. They will be re-cached when accessed again.`,\n },\n tvshows: {\n title: 'Clear TV Shows Cache',\n description: `This will permanently delete ${stats.totalCachedShows} cached TV shows. They will be re-cached when accessed again.`,\n },\n persons: {\n title: 'Clear Persons Cache',\n description: `This will permanently delete ${stats.totalCachedPersons} cached persons. They will be re-cached when accessed again.`,\n },\n search: {\n title: 'Clear Search Cache',\n description: `This will permanently delete ${stats.totalSearchCache} search queries. They will be re-cached when searched again.`,\n },\n };\n\n setConfirmDialog({\n open: true,\n type,\n title: labels[type]?.title || 'Clear Cache',\n description: labels[type]?.description || 'This action cannot be undone.',\n });\n };\n\n const clearCache = async () => {\n const cacheType = confirmDialog.type;\n setConfirmDialog({ ...confirmDialog, open: false });\n setClearing(cacheType);\n\n try {\n const db = firestore;\n let collectionName = '';\n let deletedCount = 0;\n let taskType: 'movie' | 'tv' | 'person' | 'search' = 'movie';\n let taskLabel = '';\n\n switch (cacheType) {\n case 'movies':\n collectionName = 'movies';\n deletedCount = stats.totalCachedMovies;\n taskType = 'movie';\n taskLabel = 'Deleting movies cache';\n break;\n case 'tvshows':\n collectionName = 'tvShows';\n deletedCount = stats.totalCachedShows;\n taskType = 'tv';\n taskLabel = 'Deleting TV shows cache';\n break;\n case 'persons':\n collectionName = 'persons';\n deletedCount = stats.totalCachedPersons;\n taskType = 'person';\n taskLabel = 'Deleting persons cache';\n break;\n case 'search':\n collectionName = 'searchCache';\n deletedCount = stats.totalSearchCache;\n taskType = 'search';\n taskLabel = 'Deleting search cache';\n break;\n default:\n return;\n }\n\n const tasks: DeleteTask[] = [{\n id: cacheType,\n label: taskLabel,\n type: taskType,\n status: 'pending',\n total: deletedCount,\n current: 0\n }];\n\n setDeleteTasks(tasks);\n setProgressDialogOpen(true);\n\n await new Promise(resolve => setTimeout(resolve, 50));\n\n setDeleteTasks(prev => prev.map(t => ({ ...t, status: 'deleting' as const })));\n\n const ref = collection(db, collectionName);\n const snaps = await getDocs(ref);\n\n // Delete in batches of 500 (Firestore limit)\n const batchSize = 500;\n const docs = snaps.docs;\n\n for (let i = 0; i < docs.length; i += batchSize) {\n const batch = writeBatch(db);\n const chunk = docs.slice(i, i + batchSize);\n\n chunk.forEach((doc) => {\n batch.delete(doc.ref);\n });\n\n await batch.commit();\n\n const deletedSoFar = Math.min(i + batchSize, docs.length);\n setDeleteTasks(prev => prev.map(t => ({ ...t, current: deletedSoFar })));\n }\n\n setDeleteTasks(prev => prev.map(t => ({ ...t, status: 'completed' as const })));\n\n setStats(prev => ({\n ...prev,\n ...(cacheType === 'movies' && { totalCachedMovies: 0 }),\n ...(cacheType === 'tvshows' && { totalCachedShows: 0 }),\n ...(cacheType === 'persons' && { totalCachedPersons: 0 }),\n ...(cacheType === 'search' && { totalSearchCache: 0 }),\n }));\n\n toast({\n title: 'Cache Cleared',\n description: `Successfully deleted ${deletedCount} items from ${cacheType} cache.`,\n });\n } catch (error) {\n console.error(`Error clearing ${cacheType} cache:`, error);\n setDeleteTasks([]);\n setProgressDialogOpen(false);\n toast({\n title: 'Error',\n description: `Failed to clear ${cacheType} cache. Please try again.`,\n variant: 'destructive',\n });\n } finally {\n setClearing(null);\n }\n };\n\n const handleProgressComplete = () => {\n setProgressDialogOpen(false);\n setDeleteTasks([]);\n };\n\n return (\n <div className=\"space-y-6\">\n <div className=\"flex justify-end\">\n <Button variant=\"outline\" onClick={fetchStats} disabled={isLoading} className=\"gap-2\">\n {isLoading ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <RefreshCw className=\"h-4 w-4\" />}\n Refresh Stats\n </Button>\n </div>\n\n <Card className=\"border border-border\">\n <CardHeader>\n <CardTitle>Cache Statistics</CardTitle>\n </CardHeader>\n <CardContent>\n {isLoading ? (\n <div className=\"flex justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin\" />\n </div>\n ) : (\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalCachedMovies}</p>\n <p className=\"text-sm text-muted-foreground\">Cached Movies</p>\n </div>\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalCachedShows}</p>\n <p className=\"text-sm text-muted-foreground\">Cached TV Shows</p>\n </div>\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalCachedPersons}</p>\n <p className=\"text-sm text-muted-foreground\">Cached Persons</p>\n </div>\n <div className=\"bg-muted rounded-lg p-4 text-center\">\n <p className=\"text-3xl font-bold\">{stats.totalSearchCache}</p>\n <p className=\"text-sm text-muted-foreground\">Search Cache</p>\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card className=\"border border-border\">\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <AlertCircle className=\"h-5 w-5 text-yellow-500\" />\n Cache Management\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-muted-foreground mb-6\">\n Clear cache to free up Firestore space. Cache will be rebuilt automatically when needed.\n </p>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('movies')}\n disabled={clearing === 'movies' || stats.totalCachedMovies === 0}\n className=\"gap-2\"\n >\n {clearing === 'movies' ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n Clear Movies Cache\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('tvshows')}\n disabled={clearing === 'tvshows' || stats.totalCachedShows === 0}\n className=\"gap-2\"\n >\n {clearing === 'tvshows' ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n Clear TV Shows Cache\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('persons')}\n disabled={clearing === 'persons' || stats.totalCachedPersons === 0}\n className=\"gap-2\"\n >\n {clearing === 'persons' ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n Clear Persons Cache\n </Button>\n <Button\n variant=\"destructive\"\n onClick={() => openConfirmDialog('search')}\n disabled={clearing === 'search' || stats.totalSearchCache === 0}\n className=\"gap-2\"\n >\n {clearing === 'search' ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Trash2 className=\"h-4 w-4\" />}\n Clear Search Cache\n </Button>\n </div>\n </CardContent>\n </Card>\n\n <ConfirmationDialog\n open={confirmDialog.open}\n onOpenChange={(open) => setConfirmDialog({ ...confirmDialog, open })}\n onConfirm={clearCache}\n title={confirmDialog.title}\n description={confirmDialog.description}\n confirmText=\"Clear Cache\"\n cancelText=\"Cancel\"\n variant=\"destructive\"\n />\n\n <DeleteProgressDialog\n open={progressDialogOpen}\n tasks={deleteTasks}\n onComplete={handleProgressComplete}\n />\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\admin\\toast-notifications.tsx","messages":[{"ruleId":"react-hooks/rules-of-hooks","severity":2,"message":"React Hook \"useToast\" is called in function \"showSuccessToast\" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word \"use\".","line":7,"column":21,"nodeType":"Identifier","endLine":7,"endColumn":29},{"ruleId":"react-hooks/rules-of-hooks","severity":2,"message":"React Hook \"useToast\" is called in function \"showErrorToast\" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word \"use\".","line":16,"column":21,"nodeType":"Identifier","endLine":16,"endColumn":29},{"ruleId":"react-hooks/rules-of-hooks","severity":2,"message":"React Hook \"useToast\" is called in function \"showInfoToast\" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word \"use\".","line":26,"column":21,"nodeType":"Identifier","endLine":26,"endColumn":29}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect } from 'react';\nimport { useToast } from '@/hooks/use-toast';\n\nexport function showSuccessToast(title: string, description?: string) {\n const { toast } = useToast();\n toast({\n title,\n description,\n duration: 3000,\n });\n}\n\nexport function showErrorToast(title: string, description?: string) {\n const { toast } = useToast();\n toast({\n title,\n description,\n variant: 'destructive',\n duration: 5000,\n });\n}\n\nexport function showInfoToast(title: string, description?: string) {\n const { toast } = useToast();\n toast({\n title,\n description,\n duration: 3000,\n });\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\carousel-skeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\cast-carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\content-card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\content-carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\continue-watching-carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\episode-card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\favorite-actor-button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\genre-carousel-loader.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"The ref value 'loaderRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'loaderRef.current' to a variable inside the effect, and use that variable in the cleanup function.","line":55,"column":53,"nodeType":"Identifier","endLine":55,"endColumn":60}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport { useState, useEffect, useRef, useCallback, Suspense } from \"react\";\r\nimport type { Genre } from \"@/lib/types\";\r\nimport { getGenreContent } from \"@/lib/tmdb-cached\";\r\nimport { ContentCarousel } from \"./content-carousel\";\r\nimport { CarouselSkeleton } from \"./carousel-skeleton\";\r\n\r\ninterface GenreCarouselLoaderProps {\r\n genres: Genre[];\r\n type: \"movie\" | \"tv\";\r\n}\r\n\r\nconst BATCH_SIZE = 3;\r\n\r\nfunction GenreCarousel({ genre, type }: { genre: Genre, type: \"movie\" | \"tv\" }) {\r\n const [content, setContent] = useState<any[]>([]);\r\n \r\n useEffect(() => {\r\n getGenreContent(type, genre.id.toString()).then(setContent);\r\n }, [type, genre.id]);\r\n\r\n if (content.length === 0) {\r\n return <CarouselSkeleton />;\r\n }\r\n\r\n return <ContentCarousel title={genre.name} items={content} genre={genre} type={type} />;\r\n}\r\n\r\nexport function GenreCarouselLoader({ genres, type }: GenreCarouselLoaderProps) {\r\n const [loadedCount, setLoadedCount] = useState(BATCH_SIZE);\r\n const observerRef = useRef<IntersectionObserver | null>(null);\r\n const loaderRef = useRef<HTMLDivElement | null>(null);\r\n\r\n const handleObserver = useCallback((entries: IntersectionObserverEntry[]) => {\r\n const target = entries[0];\r\n if (target.isIntersecting) {\r\n setLoadedCount((prev) => Math.min(prev + BATCH_SIZE, genres.length));\r\n }\r\n }, [genres.length]);\r\n\r\n useEffect(() => {\r\n const option = {\r\n root: null,\r\n rootMargin: \"200px\",\r\n threshold: 0,\r\n };\r\n observerRef.current = new IntersectionObserver(handleObserver, option);\r\n if (loaderRef.current) {\r\n observerRef.current.observe(loaderRef.current);\r\n }\r\n\r\n return () => {\r\n if (observerRef.current && loaderRef.current) {\r\n observerRef.current.unobserve(loaderRef.current);\r\n }\r\n }\r\n }, [handleObserver]);\r\n\r\n const genresToDisplay = genres.slice(0, loadedCount);\r\n\r\n return (\r\n <>\r\n {genresToDisplay.map((genre) => (\r\n <Suspense key={genre.id} fallback={<CarouselSkeleton />}>\r\n <GenreCarousel genre={genre} type={type} />\r\n </Suspense>\r\n ))}\r\n {loadedCount < genres.length && <div ref={loaderRef}><CarouselSkeleton /></div>}\r\n </>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\hero-carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\hero-section.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\infinite-content-grid.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"The ref value 'loaderRef.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy 'loaderRef.current' to a variable inside the effect, and use that variable in the cleanup function.","line":62,"column":38,"nodeType":"Identifier","endLine":62,"endColumn":45}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { Content } from '@/lib/types';\nimport { ContentCard } from './content-card';\nimport { Loader2 } from 'lucide-react';\n\ninterface InfiniteContentGridProps {\n initialContent: Content[];\n type: 'movie' | 'tv';\n genreId: string;\n}\n\nexport function InfiniteContentGrid({ initialContent, type, genreId }: InfiniteContentGridProps) {\n const [content, setContent] = useState(initialContent);\n const [page, setPage] = useState(2); \n const [hasMore, setHasMore] = useState(true);\n const [isLoading, setIsLoading] = useState(false);\n const loaderRef = useRef<HTMLDivElement | null>(null);\n\n const loadMoreContent = useCallback(async () => {\n if (isLoading || !hasMore) return;\n\n setIsLoading(true);\n try {\n const response = await fetch(`/api/genre?type=${type}&genreId=${genreId}&page=${page}`);\n const newContent: Content[] = await response.json();\n \n if (newContent.length > 0) {\n setContent((prev) => {\n const existingIds = new Set(prev.map(item => `${item.type}-${item.id}`));\n const uniqueNew = newContent.filter(item => !existingIds.has(`${item.type}-${item.id}`));\n return [...prev, ...uniqueNew];\n });\n setPage((prev) => prev + 1);\n } else {\n setHasMore(false);\n }\n } catch (error) {\n console.error(\"Failed to fetch more content\", error);\n } finally {\n setIsLoading(false);\n }\n }, [type, genreId, page, isLoading, hasMore]);\n\n useEffect(() => {\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting) {\n loadMoreContent();\n }\n },\n { rootMargin: '200px' }\n );\n\n if (loaderRef.current) {\n observer.observe(loaderRef.current);\n }\n\n return () => {\n if (loaderRef.current) {\n observer.unobserve(loaderRef.current);\n }\n };\n }, [loadMoreContent]);\n\n return (\n <>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4\">\n {content.map((item) => (\n <ContentCard key={`${item.type}-${item.id}`} item={item} />\n ))}\n </div>\n {hasMore && (\n <div ref={loaderRef} className=\"flex justify-center items-center py-8\">\n <Loader2 className=\"w-8 h-8 animate-spin\" />\n </div>\n )}\n </>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\known-for-carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\person-card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\recommendations.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\search-bar.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\components\\content\\search-bar.tsx:34:5\n 32 |\n 33 | useEffect(() => {\n> 34 | handleSearch(debouncedQuery);\n | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 35 | }, [debouncedQuery, handleSearch]);\n 36 |\n 37 | useEffect(() => {","line":34,"column":5,"nodeType":null,"endLine":34,"endColumn":17},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":117,"column":45,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[4618,4665],"text":"\r\n See all results for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[4618,4665],"text":"\r\n See all results for “"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[4618,4665],"text":"\r\n See all results for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[4618,4665],"text":"\r\n See all results for ”"},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":117,"column":53,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[4672,4697],"text":""\r\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[4672,4697],"text":"“\r\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[4672,4697],"text":""\r\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[4672,4697],"text":"”\r\n "},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":135,"column":32,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[5371,5405],"text":"\r\n No results for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[5371,5405],"text":"\r\n No results for “"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[5371,5405],"text":"\r\n No results for ""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[5371,5405],"text":"\r\n No results for ”"},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":135,"column":40,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[5412,5429],"text":""\r\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[5412,5429],"text":"“\r\n "},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[5412,5429],"text":""\r\n "},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[5412,5429],"text":"”\r\n "},"desc":"Replace with `”`."}]}],"suppressedMessages":[],"errorCount":5,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\";\r\n\r\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\r\nimport { useRouter } from \"next/navigation\";\r\nimport Image from \"next/image\";\r\nimport Link from \"next/link\";\r\nimport { Search, Film, Tv, Loader2 } from \"lucide-react\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { searchContentCached as searchContent } from \"@/lib/tmdb-cached\";\r\nimport type { Content } from \"@/lib/types\";\r\nimport { useDebounce } from \"@/hooks/use-debounce\";\r\n\r\nexport function SearchBar() {\r\n const router = useRouter();\r\n const [query, setQuery] = useState(\"\");\r\n const [results, setResults] = useState<Content[]>([]);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [isFocused, setIsFocused] = useState(false);\r\n const debouncedQuery = useDebounce(query, 300);\r\n const searchRef = useRef<HTMLDivElement>(null);\r\n\r\n const handleSearch = useCallback(async (searchQuery: string) => {\r\n if (searchQuery.trim().length > 1) {\r\n setIsLoading(true);\r\n const searchResults = await searchContent(searchQuery);\r\n setResults(searchResults.slice(0, 7));\r\n setIsLoading(false);\r\n } else {\r\n setResults([]);\r\n }\r\n }, []);\r\n\r\n useEffect(() => {\r\n handleSearch(debouncedQuery);\r\n }, [debouncedQuery, handleSearch]);\r\n\r\n useEffect(() => {\r\n const handleClickOutside = (event: MouseEvent) => {\r\n if (searchRef.current && !searchRef.current.contains(event.target as Node)) {\r\n setIsFocused(false);\r\n }\r\n };\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n return () => {\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n };\r\n }, []);\r\n\r\n const handleResultClick = () => {\r\n setQuery(\"\");\r\n setResults([]);\r\n setIsFocused(false);\r\n };\r\n \r\n const handleLegacySearch = (event: React.FormEvent<HTMLFormElement>) => {\r\n event.preventDefault();\r\n if (query.trim()) {\r\n router.push(`/search?q=${encodeURIComponent(query)}`);\r\n handleResultClick();\r\n }\r\n };\r\n\r\n return (\r\n <div ref={searchRef} className=\"relative w-full max-w-xs\">\r\n <form onSubmit={handleLegacySearch}>\r\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\r\n <Input\r\n type=\"search\"\r\n name=\"query\"\r\n placeholder=\"Search titles...\"\r\n className=\"pl-9 bg-background/50 border-border\"\r\n value={query}\r\n onChange={(e) => setQuery(e.target.value)}\r\n onFocus={() => setIsFocused(true)}\r\n />\r\n {isLoading && <Loader2 className=\"absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 animate-spin text-muted-foreground\" />}\r\n </form>\r\n\r\n {isFocused && (query.length > 1 || results.length > 0) && (\r\n <div className=\"absolute top-full mt-2 w-full bg-background border border-border rounded-lg shadow-lg z-50 max-h-96 overflow-y-auto\">\r\n {results.length > 0 ? (\r\n <ul>\r\n {results.map((item) => (\r\n <li key={item.id}>\r\n <Link\r\n href={`/${item.type}/${item.id}`}\r\n onClick={handleResultClick}\r\n className=\"flex items-center p-2 hover:bg-accent\"\r\n >\r\n <div className=\"relative w-12 h-16 mr-3 flex-shrink-0\">\r\n <Image\r\n src={item.poster_path}\r\n alt={item.title || item.name || \"Poster\"}\r\n fill\r\n className=\"object-cover rounded-sm\"\r\n sizes=\"50px\"\r\n />\r\n </div>\r\n <div className=\"flex-grow overflow-hidden\">\r\n <p className=\"font-semibold truncate\">{item.title || item.name}</p>\r\n <div className=\"flex items-center text-xs text-muted-foreground\">\r\n {item.type === 'movie' ? <Film className=\"h-3 w-3 mr-1\" /> : <Tv className=\"h-3 w-3 mr-1\" />}\r\n <span>{item.release_date?.substring(0, 4) || item.first_air_date?.substring(0, 4)}</span>\r\n </div>\r\n </div>\r\n </Link>\r\n </li>\r\n ))}\r\n {query.trim() && (\r\n <>\r\n <li>\r\n <Link\r\n href={`/search?q=${encodeURIComponent(query)}`}\r\n onClick={handleResultClick}\r\n className=\"block p-2 text-center text-sm text-primary hover:bg-accent\"\r\n >\r\n See all results for \"{query}\"\r\n </Link>\r\n </li>\r\n <li>\r\n <Link\r\n href={`/search/advanced?q=${encodeURIComponent(query)}`}\r\n onClick={handleResultClick}\r\n className=\"block p-2 text-center text-sm text-muted-foreground hover:bg-accent hover:text-primary border-t\"\r\n >\r\n Advanced search\r\n </Link>\r\n </li>\r\n </>\r\n )}\r\n </ul>\r\n ) : (\r\n !isLoading && query.length > 1 && (\r\n <p className=\"p-4 text-sm text-center text-muted-foreground\">\r\n No results for \"{query}\"\r\n </p>\r\n )\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\star-rating.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\trailer-button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\video-player.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\watch-providers.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\watched-button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\content\\watchlist-button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\error-boundary.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\feedback.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\back-button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\back-to-top-button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\footer.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\header.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\mobile-nav.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\theme-toggle.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\components\\layout\\theme-toggle.tsx:13:5\n 11 |\n 12 | useEffect(() => {\n> 13 | setMounted(true);\n | ^^^^^^^^^^ Avoid calling setState() directly within an effect\n 14 | }, []);\n 15 |\n 16 | if (!mounted) {","line":13,"column":5,"nodeType":null,"endLine":13,"endColumn":15}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useTheme } from 'next-themes';\nimport { Sun, Moon } from 'lucide-react';\nimport { Button } from '@/components/ui/button';\nimport { useEffect, useState } from 'react';\n\nexport function ThemeToggle() {\n const [mounted, setMounted] = useState(false);\n const { theme, setTheme } = useTheme();\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n if (!mounted) {\n return <div className=\"w-10 h-10\" />;\n }\n\n const isDark = theme === 'dark';\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setTheme(isDark ? 'light' : 'dark')}\n className=\"rounded-full transition-colors hover:bg-accent\"\n aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`}\n >\n {isDark ? (\n <Sun className=\"h-5 w-5 transition-transform duration-300\" />\n ) : (\n <Moon className=\"h-5 w-5 transition-transform duration-300\" />\n )}\n </Button>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\layout\\user-menu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\loading-skeletons.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\profile\\profile-edit-form.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\profile\\profile-picture-upload.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\profile\\profile-view.tsx","messages":[{"ruleId":"react-hooks/purity","severity":2,"message":"Error: Cannot call impure function during render\n\n`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).\n\nD:\\Visual_Code\\StreamVerse\\src\\components\\profile\\profile-view.tsx:18:55\n 16 |\n 17 | const displayUrl = profile?.profilePicture || user?.photoURL;\n> 18 | const memberDays = profile?.createdAt ? Math.floor((Date.now() - profile.createdAt) / (1000 * 60 * 60 * 24)) : 0;\n | ^^^^^^^^^^ Cannot call impure function\n 19 |\n 20 | return (\n 21 | <div className=\"relative mb-12\">","line":18,"column":55,"nodeType":null,"endLine":18,"endColumn":65}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useUser } from '@/firebase/provider';\nimport { useUserProfile } from '@/context/user-profile-context';\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';\nimport { Button } from '@/components/ui/button';\nimport { User, Edit2, Calendar, Mail, Users } from 'lucide-react';\n\ninterface ProfileViewProps {\n onEditClick: () => void;\n}\n\nexport function ProfileView({ onEditClick }: ProfileViewProps) {\n const { user } = useUser();\n const { profile } = useUserProfile();\n\n const displayUrl = profile?.profilePicture || user?.photoURL;\n const memberDays = profile?.createdAt ? Math.floor((Date.now() - profile.createdAt) / (1000 * 60 * 60 * 24)) : 0;\n\n return (\n <div className=\"relative mb-12\">\n <div className=\"relative h-40 md:h-48 bg-gradient-to-r from-primary/20 via-primary/10 to-transparent rounded-2xl overflow-hidden\">\n <div className=\"absolute inset-0 bg-gradient-to-br from-blue-500/10 to-purple-500/10 backdrop-blur-sm\" />\n </div>\n\n <div className=\"relative -mt-20 md:-mt-24 px-4 md:px-8\">\n <div className=\"flex flex-col md:flex-row md:items-end gap-4 md:gap-8 mb-8\">\n <div className=\"flex-shrink-0\">\n <div className=\"relative inline-block\">\n <Avatar className=\"h-40 w-40 md:h-48 md:w-48 border-4 border-background shadow-2xl ring-2 ring-primary/30\">\n {displayUrl ? (\n <AvatarImage src={displayUrl} alt={profile?.displayName || 'User'} />\n ) : null}\n <AvatarFallback className=\"text-3xl\">\n <User className=\"h-20 w-20\" />\n </AvatarFallback>\n </Avatar>\n {displayUrl && (\n <div className=\"absolute bottom-2 right-2 w-5 h-5 bg-green-500 rounded-full border-2 border-background\" />\n )}\n </div>\n </div>\n\n <div className=\"flex-1 pb-4\">\n <div className=\"space-y-3\">\n <h1 className=\"text-3xl md:text-4xl font-bold bg-gradient-to-r from-primary to-blue-400 bg-clip-text text-transparent\">\n {profile?.displayName || user?.displayName || 'User'}\n </h1>\n {profile?.username && (\n <p className=\"text-lg text-muted-foreground\">@{profile.username}</p>\n )}\n {profile?.bio && (\n <p className=\"text-base text-foreground/80 max-w-lg leading-relaxed\">{profile.bio}</p>\n )}\n </div>\n </div>\n\n <Button\n onClick={onEditClick}\n size=\"lg\"\n className=\"w-full md:w-auto md:self-center gap-2 rounded-full bg-primary hover:bg-primary/90 transition-all duration-300 shadow-lg hover:shadow-xl\"\n >\n <Edit2 className=\"h-5 w-5\" />\n <span className=\"text-base\">Edit Profile</span>\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div className=\"bg-card/50 backdrop-blur border border-border/50 rounded-xl p-6 hover:bg-card/80 transition-all duration-300\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Mail className=\"h-5 w-5\" />\n <span className=\"text-sm font-medium\">Email</span>\n </div>\n <p className=\"text-sm font-semibold break-all text-foreground/90\">{user?.email}</p>\n </div>\n </div>\n\n {profile?.createdAt && (\n <div className=\"bg-card/50 backdrop-blur border border-border/50 rounded-xl p-6 hover:bg-card/80 transition-all duration-300\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Calendar className=\"h-5 w-5\" />\n <span className=\"text-sm font-medium\">Member Since</span>\n </div>\n <p className=\"text-sm font-semibold text-foreground/90\">\n {new Date(profile.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </p>\n <p className=\"text-xs text-muted-foreground\">{memberDays} days ago</p>\n </div>\n </div>\n )}\n\n <div className=\"bg-card/50 backdrop-blur border border-border/50 rounded-xl p-6 hover:bg-card/80 transition-all duration-300\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Users className=\"h-5 w-5\" />\n <span className=\"text-sm font-medium\">Account Status</span>\n </div>\n <p className=\"text-sm font-semibold text-green-500\">Active</p>\n <p className=\"text-xs text-muted-foreground\">All systems active</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\profile\\theme-selector.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":51,"column":108,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2055,2116],"text":" Reset to your workspace's custom color theme\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[2055,2116],"text":" Reset to your workspace‘s custom color theme\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2055,2116],"text":" Reset to your workspace's custom color theme\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[2055,2116],"text":" Reset to your workspace’s custom color theme\r\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":69,"column":134,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2755,2828],"text":" Your color theme is set to your workspace's custom color\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[2755,2828],"text":" Your color theme is set to your workspace‘s custom color\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[2755,2828],"text":" Your color theme is set to your workspace's custom color\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[2755,2828],"text":" Your color theme is set to your workspace’s custom color\r\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":156,"column":129,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6716,6850],"text":" Choose a preset theme or reset to your workspace's default color. Changes apply instantly and sync across all your devices.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[6716,6850],"text":" Choose a preset theme or reset to your workspace‘s default color. Changes apply instantly and sync across all your devices.\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6716,6850],"text":" Choose a preset theme or reset to your workspace's default color. Changes apply instantly and sync across all your devices.\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[6716,6850],"text":" Choose a preset theme or reset to your workspace’s default color. Changes apply instantly and sync across all your devices.\r\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { Check, Palette, RotateCcw, Zap } from 'lucide-react';\r\nimport { Button } from '@/components/ui/button';\r\nimport { useThemeColor } from '@/hooks/use-theme-color';\r\nimport { useAdminColors } from '@/hooks/use-admin-colors';\r\nimport { getAllThemePresets } from '@/lib/theme-config';\r\n\r\nexport function ThemeSelector() {\r\n const { currentTheme, setTheme, resetToAdminDefault, isUpdating, usingAdminDefault } = useThemeColor();\r\n const { adminHsl } = useAdminColors();\r\n const presets = getAllThemePresets();\r\n\r\n const handleThemeSelect = async (themeId: string) => {\r\n await setTheme(themeId);\r\n };\r\n\r\n const handleResetToDefault = async () => {\r\n await resetToAdminDefault();\r\n };\r\n\r\n return (\r\n <div className=\"space-y-8\">\r\n <div className=\"space-y-3\">\r\n <div className=\"flex items-center gap-3\">\r\n <div className=\"p-2 rounded-lg bg-primary/10\">\r\n <Palette className=\"h-5 w-5 text-primary\" />\r\n </div>\r\n <div>\r\n <h2 className=\"text-2xl font-bold\">Appearance</h2>\r\n <p className=\"text-sm text-muted-foreground\">Customize your color theme across the entire app</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"bg-card/50 backdrop-blur border border-border/50 rounded-2xl p-8\">\r\n <div className=\"space-y-6\">\r\n <div>\r\n <h3 className=\"text-base font-semibold mb-4 flex items-center gap-2\">\r\n <Zap className=\"h-4 w-4\" />\r\n Select Your Theme\r\n </h3>\r\n <p className=\"text-sm text-muted-foreground mb-6\">\r\n Choose a color palette that matches your style. Your selection syncs across all devices.\r\n </p>\r\n </div>\r\n\r\n {!usingAdminDefault && (\r\n <div className=\"flex items-center gap-3 p-3 bg-amber-500/10 border border-amber-500/20 rounded-lg\">\r\n <p className=\"text-sm text-muted-foreground flex-1\">\r\n <span className=\"font-medium text-foreground\">Admin Default:</span> Reset to your workspace's custom color theme\r\n </p>\r\n <Button\r\n variant=\"ghost\"\r\n size=\"sm\"\r\n onClick={handleResetToDefault}\r\n disabled={isUpdating}\r\n className=\"gap-2\"\r\n >\r\n <RotateCcw className=\"h-4 w-4\" />\r\n Reset\r\n </Button>\r\n </div>\r\n )}\r\n\r\n {usingAdminDefault && (\r\n <div className=\"p-4 bg-green-500/10 border border-green-500/20 rounded-lg\">\r\n <p className=\"text-sm text-muted-foreground\">\r\n <span className=\"font-medium text-foreground\">苇蔚违 Using Admin Default:</span> Your color theme is set to your workspace's custom color\r\n </p>\r\n </div>\r\n )}\r\n\r\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\r\n {presets.map((preset) => {\r\n const isSelected = currentTheme === preset.id;\r\n const hslColor = `hsl(${preset.hslValue})`;\r\n\r\n return (\r\n <button\r\n key={preset.id}\r\n onClick={() => handleThemeSelect(preset.id)}\r\n disabled={isUpdating}\r\n className={`relative group overflow-hidden rounded-xl border-2 transition-all duration-300 p-4 text-left ${\r\n isSelected\r\n ? 'border-primary bg-primary/5'\r\n : 'border-border/50 hover:border-border hover:bg-card/80'\r\n } ${isUpdating ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}\r\n >\r\n <div className=\"flex items-start justify-between mb-4\">\r\n <div className=\"flex-1\">\r\n <h4 className=\"font-semibold text-foreground mb-1\">{preset.name}</h4>\r\n <p className=\"text-xs text-muted-foreground\">{preset.description}</p>\r\n </div>\r\n {isSelected && (\r\n <div className=\"flex-shrink-0 ml-2\">\r\n <div className=\"relative\">\r\n <div className=\"absolute -inset-1 bg-primary rounded-full opacity-20 blur\"></div>\r\n <div className=\"relative bg-primary rounded-full p-1.5\">\r\n <Check className=\"h-4 w-4 text-primary-foreground\" />\r\n </div>\r\n </div>\r\n </div>\r\n )}\r\n </div>\r\n\r\n <div className=\"space-y-3\">\r\n <div className=\"rounded-lg overflow-hidden border border-border/50\">\r\n <div className=\"h-12 flex\">\r\n <div\r\n className=\"flex-1 transition-all duration-300\"\r\n style={{ backgroundColor: hslColor }}\r\n />\r\n <div className=\"flex-1 bg-muted/30\" />\r\n </div>\r\n </div>\r\n\r\n <div className=\"space-y-2\">\r\n <div className=\"flex gap-2\">\r\n <button\r\n className=\"flex-1 h-8 rounded-md text-xs font-medium transition-all duration-300\"\r\n style={{\r\n backgroundColor: hslColor,\r\n color: 'white',\r\n opacity: isSelected ? 1 : 0.7,\r\n }}\r\n disabled\r\n >\r\n Button\r\n </button>\r\n <div\r\n className=\"flex-1 h-8 rounded-md border text-xs font-medium flex items-center justify-center\"\r\n style={{\r\n borderColor: hslColor,\r\n color: hslColor,\r\n }}\r\n >\r\n Link\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n {isSelected && (\r\n <div className=\"absolute inset-0 pointer-events-none rounded-xl ring-2 ring-primary/50\" />\r\n )}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"bg-blue-500/5 border border-blue-500/20 rounded-lg p-4\">\r\n <p className=\"text-xs text-muted-foreground\">\r\n 螐胃韦魏 <span className=\"font-medium text-foreground\">How it works:</span> Choose a preset theme or reset to your workspace's default color. Changes apply instantly and sync across all your devices.\r\n </p>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\search\\advanced-search-filters.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\theme-initializer.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\components\\theme-initializer.tsx:29:5\n 27 | }\n 28 |\n> 29 | setInitialized(true);\n | ^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 30 | }, [adminHsl]);\n 31 |\n 32 | if (!initialized) {","line":29,"column":5,"nodeType":null,"endLine":29,"endColumn":19}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useEffect, useState } from 'react';\nimport { applyTheme, DEFAULT_THEME } from '@/lib/theme-config';\nimport { useAdminColors } from '@/hooks/use-admin-colors';\n\nconst STORAGE_KEY = 'user-theme';\n\nexport function ThemeInitializer() {\n const { adminHsl, isLoading } = useAdminColors();\n const [initialized, setInitialized] = useState(false);\n\n useEffect(() => {\n const savedTheme = localStorage.getItem(STORAGE_KEY);\n\n if (savedTheme) {\n applyTheme(savedTheme);\n } else {\n if (adminHsl) {\n if (typeof document !== 'undefined') {\n document.documentElement.style.setProperty('--primary', adminHsl);\n document.documentElement.style.setProperty('--ring', adminHsl);\n }\n } else {\n applyTheme(DEFAULT_THEME);\n }\n }\n\n setInitialized(true);\n }, [adminHsl]);\n\n if (!initialized) {\n return null;\n }\n\n return null;\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\theme-provider.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\accordion.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\alert-dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\alert.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\aspect-ratio.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\avatar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\badge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\calendar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\chart.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\checkbox.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\collapsible.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\dropdown-menu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\form.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\input.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\label.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\menubar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\popover.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\progress.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\radio-group.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\scroll-area.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\select.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\separator.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\sheet.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\sidebar.tsx","messages":[{"ruleId":"react-hooks/purity","severity":2,"message":"Error: Cannot call impure function during render\n\n`Math.random` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).\n\nD:\\Visual_Code\\StreamVerse\\src\\components\\ui\\sidebar.tsx:655:26\n 653 | // Random width between 50 to 90%.\n 654 | const width = React.useMemo(() => {\n> 655 | return `${Math.floor(Math.random() * 40) + 50}%`\n | ^^^^^^^^^^^^^ Cannot call impure function\n 656 | }, [])\n 657 |\n 658 | return (","line":655,"column":26,"nodeType":null,"endLine":655,"endColumn":39}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { VariantProps, cva } from \"class-variance-authority\"\nimport { PanelLeft } from \"lucide-react\"\n\nimport { useIsMobile } from \"@/hooks/use-mobile\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport { Separator } from \"@/components/ui/separator\"\nimport { Sheet, SheetContent } from \"@/components/ui/sheet\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\"\n\nconst SIDEBAR_COOKIE_NAME = \"sidebar_state\"\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\nconst SIDEBAR_WIDTH = \"16rem\"\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\"\nconst SIDEBAR_WIDTH_ICON = \"3rem\"\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\"\n\ntype SidebarContext = {\n state: \"expanded\" | \"collapsed\"\n open: boolean\n setOpen: (open: boolean) => void\n openMobile: boolean\n setOpenMobile: (open: boolean) => void\n isMobile: boolean\n toggleSidebar: () => void\n}\n\nconst SidebarContext = React.createContext<SidebarContext | null>(null)\n\nfunction useSidebar() {\n const context = React.useContext(SidebarContext)\n if (!context) {\n throw new Error(\"useSidebar must be used within a SidebarProvider.\")\n }\n\n return context\n}\n\nconst SidebarProvider = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean\n open?: boolean\n onOpenChange?: (open: boolean) => void\n }\n>(\n (\n {\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n },\n ref\n ) => {\n const isMobile = useIsMobile()\n const [openMobile, setOpenMobile] = React.useState(false)\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen)\n const open = openProp ?? _open\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === \"function\" ? value(open) : value\n if (setOpenProp) {\n setOpenProp(openState)\n } else {\n _setOpen(openState)\n }\n\n // This sets the cookie to keep the sidebar state.\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n },\n [setOpenProp, open]\n )\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile\n ? setOpenMobile((open) => !open)\n : setOpen((open) => !open)\n }, [isMobile, setOpen, setOpenMobile])\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (\n event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n (event.metaKey || event.ctrlKey)\n ) {\n event.preventDefault()\n toggleSidebar()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [toggleSidebar])\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? \"expanded\" : \"collapsed\"\n\n const contextValue = React.useMemo<SidebarContext>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]\n )\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <TooltipProvider delayDuration={0}>\n <div\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </TooltipProvider>\n </SidebarContext.Provider>\n )\n }\n)\nSidebarProvider.displayName = \"SidebarProvider\"\n\nconst Sidebar = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\"\n variant?: \"sidebar\" | \"floating\" | \"inset\"\n collapsible?: \"offcanvas\" | \"icon\" | \"none\"\n }\n>(\n (\n {\n side = \"left\",\n variant = \"sidebar\",\n collapsible = \"offcanvas\",\n className,\n children,\n ...props\n },\n ref\n ) => {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar()\n\n if (collapsible === \"none\") {\n return (\n <div\n className={cn(\n \"flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n )\n }\n\n if (isMobile) {\n return (\n <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n <SheetContent\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden\"\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n side={side}\n >\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <div\n ref={ref}\n className=\"group peer hidden md:block text-sidebar-foreground\"\n data-state={state}\n data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n data-variant={variant}\n data-side={side}\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={cn(\n \"duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear\",\n \"group-data-[collapsible=offcanvas]:w-0\",\n \"group-data-[side=right]:rotate-180\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]\"\n : \"group-data-[collapsible=icon]:w-[--sidebar-width-icon]\"\n )}\n />\n <div\n className={cn(\n \"duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex\",\n side === \"left\"\n ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n // Adjust the padding for floating and inset variants.\n variant === \"floating\" || variant === \"inset\"\n ? \"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]\"\n : \"group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l\",\n className\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow\"\n >\n {children}\n </div>\n </div>\n </div>\n )\n }\n)\nSidebar.displayName = \"Sidebar\"\n\nconst SidebarTrigger = React.forwardRef<\n React.ElementRef<typeof Button>,\n React.ComponentProps<typeof Button>\n>(({ className, onClick, ...props }, ref) => {\n const { toggleSidebar } = useSidebar()\n\n return (\n <Button\n ref={ref}\n data-sidebar=\"trigger\"\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-7 w-7\", className)}\n onClick={(event) => {\n onClick?.(event)\n toggleSidebar()\n }}\n {...props}\n >\n <PanelLeft />\n <span className=\"sr-only\">Toggle Sidebar</span>\n </Button>\n )\n})\nSidebarTrigger.displayName = \"SidebarTrigger\"\n\nconst SidebarRail = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\">\n>(({ className, ...props }, ref) => {\n const { toggleSidebar } = useSidebar()\n\n return (\n <button\n ref={ref}\n data-sidebar=\"rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n \"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex\",\n \"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize\",\n \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n \"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar\",\n \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarRail.displayName = \"SidebarRail\"\n\nconst SidebarInset = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"main\">\n>(({ className, ...props }, ref) => {\n return (\n <main\n ref={ref}\n className={cn(\n \"relative flex min-h-svh flex-1 flex-col bg-background\",\n \"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarInset.displayName = \"SidebarInset\"\n\nconst SidebarInput = React.forwardRef<\n React.ElementRef<typeof Input>,\n React.ComponentProps<typeof Input>\n>(({ className, ...props }, ref) => {\n return (\n <Input\n ref={ref}\n data-sidebar=\"input\"\n className={cn(\n \"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarInput.displayName = \"SidebarInput\"\n\nconst SidebarHeader = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"header\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n )\n})\nSidebarHeader.displayName = \"SidebarHeader\"\n\nconst SidebarFooter = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"footer\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n )\n})\nSidebarFooter.displayName = \"SidebarFooter\"\n\nconst SidebarSeparator = React.forwardRef<\n React.ElementRef<typeof Separator>,\n React.ComponentProps<typeof Separator>\n>(({ className, ...props }, ref) => {\n return (\n <Separator\n ref={ref}\n data-sidebar=\"separator\"\n className={cn(\"mx-2 w-auto bg-sidebar-border\", className)}\n {...props}\n />\n )\n})\nSidebarSeparator.displayName = \"SidebarSeparator\"\n\nconst SidebarContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"content\"\n className={cn(\n \"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarContent.displayName = \"SidebarContent\"\n\nconst SidebarGroup = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"group\"\n className={cn(\"relative flex w-full min-w-0 flex-col p-2\", className)}\n {...props}\n />\n )\n})\nSidebarGroup.displayName = \"SidebarGroup\"\n\nconst SidebarGroupLabel = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"div\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-label\"\n className={cn(\n \"duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarGroupLabel.displayName = \"SidebarGroupLabel\"\n\nconst SidebarGroupAction = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-action\"\n className={cn(\n \"absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 after:md:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarGroupAction.displayName = \"SidebarGroupAction\"\n\nconst SidebarGroupContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\"w-full text-sm\", className)}\n {...props}\n />\n))\nSidebarGroupContent.displayName = \"SidebarGroupContent\"\n\nconst SidebarMenu = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu\"\n className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n {...props}\n />\n))\nSidebarMenu.displayName = \"SidebarMenu\"\n\nconst SidebarMenuItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li\n ref={ref}\n data-sidebar=\"menu-item\"\n className={cn(\"group/menu-item relative\", className)}\n {...props}\n />\n))\nSidebarMenuItem.displayName = \"SidebarMenuItem\"\n\nconst sidebarMenuButtonVariants = cva(\n \"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground\",\n outline:\n \"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]\",\n },\n size: {\n default: \"h-8 text-sm\",\n sm: \"h-7 text-xs\",\n lg: \"h-12 text-sm group-data-[collapsible=icon]:!p-0\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nconst SidebarMenuButton = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean\n isActive?: boolean\n tooltip?: string | React.ComponentProps<typeof TooltipContent>\n } & VariantProps<typeof sidebarMenuButtonVariants>\n>(\n (\n {\n asChild = false,\n isActive = false,\n variant = \"default\",\n size = \"default\",\n tooltip,\n className,\n ...props\n },\n ref\n ) => {\n const Comp = asChild ? Slot : \"button\"\n const { isMobile, state } = useSidebar()\n\n const button = (\n <Comp\n ref={ref}\n data-sidebar=\"menu-button\"\n data-size={size}\n data-active={isActive}\n className={cn(sidebarMenuButtonVariants({ variant, size }), className)}\n {...props}\n />\n )\n\n if (!tooltip) {\n return button\n }\n\n if (typeof tooltip === \"string\") {\n tooltip = {\n children: tooltip,\n }\n }\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent\n side=\"right\"\n align=\"center\"\n hidden={state !== \"collapsed\" || isMobile}\n {...tooltip}\n />\n </Tooltip>\n )\n }\n)\nSidebarMenuButton.displayName = \"SidebarMenuButton\"\n\nconst SidebarMenuAction = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean\n showOnHover?: boolean\n }\n>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-action\"\n className={cn(\n \"absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 after:md:hidden\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n showOnHover &&\n \"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarMenuAction.displayName = \"SidebarMenuAction\"\n\nconst SidebarMenuBadge = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"menu-badge\"\n className={cn(\n \"absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none\",\n \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n))\nSidebarMenuBadge.displayName = \"SidebarMenuBadge\"\n\nconst SidebarMenuSkeleton = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean\n }\n>(({ className, showIcon = false, ...props }, ref) => {\n // Random width between 50 to 90%.\n const width = React.useMemo(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`\n }, [])\n\n return (\n <div\n ref={ref}\n data-sidebar=\"menu-skeleton\"\n className={cn(\"rounded-md h-8 flex gap-2 px-2 items-center\", className)}\n {...props}\n >\n {showIcon && (\n <Skeleton\n className=\"size-4 rounded-md\"\n data-sidebar=\"menu-skeleton-icon\"\n />\n )}\n <Skeleton\n className=\"h-4 flex-1 max-w-[--skeleton-width]\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n \"--skeleton-width\": width,\n } as React.CSSProperties\n }\n />\n </div>\n )\n})\nSidebarMenuSkeleton.displayName = \"SidebarMenuSkeleton\"\n\nconst SidebarMenuSub = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu-sub\"\n className={cn(\n \"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n))\nSidebarMenuSub.displayName = \"SidebarMenuSub\"\n\nconst SidebarMenuSubItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ ...props }, ref) => <li ref={ref} {...props} />)\nSidebarMenuSubItem.displayName = \"SidebarMenuSubItem\"\n\nconst SidebarMenuSubButton = React.forwardRef<\n HTMLAnchorElement,\n React.ComponentProps<\"a\"> & {\n asChild?: boolean\n size?: \"sm\" | \"md\"\n isActive?: boolean\n }\n>(({ asChild = false, size = \"md\", isActive, className, ...props }, ref) => {\n const Comp = asChild ? Slot : \"a\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-sub-button\"\n data-size={size}\n data-active={isActive}\n className={cn(\n \"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground\",\n \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n size === \"sm\" && \"text-xs\",\n size === \"md\" && \"text-sm\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarMenuSubButton.displayName = \"SidebarMenuSubButton\"\n\nexport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupAction,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInput,\n SidebarInset,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarProvider,\n SidebarRail,\n SidebarSeparator,\n SidebarTrigger,\n useSidebar,\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\skeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\slider.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\switch.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\table.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\tabs.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\textarea.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\toast.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\toaster.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\components\\ui\\tooltip.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\context\\favorite-actors-context.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\context\\history-context.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\context\\rating-context.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\context\\user-profile-context.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\context\\watchlist-context.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\context\\watchlist-context.tsx:36:5\n 34 |\n 35 | useEffect(() => {\n> 36 | setHasReceivedInitialSnapshot(false);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 37 | }, [user?.uid]);\n 38 |\n 39 | const watchlistDocRef = useMemoFirebase(() => {","line":36,"column":5,"nodeType":null,"endLine":36,"endColumn":34},{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\context\\watchlist-context.tsx:51:9\n 49 | if (watchlistDocRef && (watchlistDoc !== null || !isWatchlistLoading)) {\n 50 | if (!hasReceivedInitialSnapshot) {\n> 51 | setHasReceivedInitialSnapshot(true);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 52 | }\n 53 | }\n 54 | }, [watchlistDoc, isWatchlistLoading, watchlistDocRef, hasReceivedInitialSnapshot]);","line":51,"column":9,"nodeType":null,"endLine":51,"endColumn":38}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport React, { createContext, useContext, ReactNode, useMemo, useEffect, useState, useCallback } from 'react';\r\nimport type { Content } from '@/lib/types';\r\nimport { useToast } from \"@/hooks/use-toast\";\r\nimport { useUser, useFirestore, useMemoFirebase } from '@/firebase/provider';\r\nimport { collection, doc, arrayUnion, arrayRemove, setDoc, updateDoc } from 'firebase/firestore';\r\nimport { useDoc } from '@/firebase/firestore/use-doc';\r\n\r\ninterface WatchlistItem {\r\n id: number;\r\n type: 'movie' | 'tv';\r\n}\r\n\r\ninterface WatchlistDoc {\r\n items: WatchlistItem[];\r\n}\r\n\r\ninterface WatchlistContextType {\r\n watchlist: WatchlistItem[];\r\n addToWatchlist: (item: Content) => Promise<void>;\r\n removeFromWatchlist: (id: number, type: 'movie' | 'tv') => Promise<void>;\r\n isInWatchlist: (id: number, type: 'movie' | 'tv') => boolean;\r\n isLoading: boolean;\r\n}\r\n\r\nconst WatchlistContext = createContext<WatchlistContextType | undefined>(undefined);\r\n\r\nexport const WatchlistProvider = ({ children }: { children: ReactNode }) => {\r\n const { toast } = useToast();\r\n const { user, isAuthReady } = useUser();\r\n const firestore = useFirestore();\r\n const [hasReceivedInitialSnapshot, setHasReceivedInitialSnapshot] = useState(false);\r\n\r\n useEffect(() => {\r\n setHasReceivedInitialSnapshot(false);\r\n }, [user?.uid]);\r\n\r\n const watchlistDocRef = useMemoFirebase(() => {\r\n if (!user || !firestore || !isAuthReady) {\r\n return null;\r\n }\r\n return doc(collection(firestore, 'users', user.uid, 'watchlist'), 'items');\r\n }, [user, firestore, isAuthReady]);\r\n\r\n const { data: watchlistDoc, isLoading: isWatchlistLoading } = useDoc<WatchlistDoc>(watchlistDocRef);\r\n\r\n useEffect(() => {\r\n if (watchlistDocRef && (watchlistDoc !== null || !isWatchlistLoading)) {\r\n if (!hasReceivedInitialSnapshot) {\r\n setHasReceivedInitialSnapshot(true);\r\n }\r\n }\r\n }, [watchlistDoc, isWatchlistLoading, watchlistDocRef, hasReceivedInitialSnapshot]);\r\n\r\n useEffect(() => {\r\n const createWatchlistIfNeeded = async () => {\r\n const shouldCreateDocument = user &&\r\n isAuthReady &&\r\n !isWatchlistLoading &&\r\n hasReceivedInitialSnapshot &&\r\n !watchlistDoc &&\r\n watchlistDocRef;\r\n\r\n if (shouldCreateDocument) {\r\n try {\r\n await setDoc(watchlistDocRef, { items: [] }, { merge: true });\r\n } catch (error) {\r\n console.error(\"Error creating watchlist document:\", error);\r\n toast({\r\n variant: \"destructive\",\r\n title: \"Database Error\",\r\n description: \"Failed to initialize your watchlist. Please try logging in again.\"\r\n });\r\n }\r\n }\r\n };\r\n createWatchlistIfNeeded();\r\n }, [user, isAuthReady, isWatchlistLoading, hasReceivedInitialSnapshot, watchlistDoc, watchlistDocRef, toast]);\r\n\r\n const watchlist = useMemo(() => watchlistDoc?.items || [], [watchlistDoc]);\r\n\r\n const watchlistSet = useMemo(() => {\r\n const set = new Set<string>();\r\n watchlist.forEach(item => set.add(`${item.type}-${item.id}`));\r\n return set;\r\n }, [watchlist]);\r\n\r\n const addToWatchlist = useCallback(async (item: Content) => {\r\n if (!user || !isAuthReady) {\r\n toast({\r\n variant: \"destructive\",\r\n title: \"Please log in\",\r\n description: \"You need to be logged in to add items to your watchlist.\",\r\n });\r\n return;\r\n }\r\n if (!watchlistDocRef) return;\r\n\r\n const watchlistItem = { id: item.id, type: item.type };\r\n try {\r\n await updateDoc(watchlistDocRef, { items: arrayUnion(watchlistItem) });\r\n toast({ title: \"Added to My List\", description: `${item.title || item.name} has been added.` });\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\r\n console.error(\"Error adding to watchlist:\", error);\r\n toast({ variant: \"destructive\", title: \"Error\", description: errorMessage });\r\n }\r\n }, [user, isAuthReady, watchlistDocRef, toast]);\r\n\r\n const removeFromWatchlist = useCallback(async (id: number, type: 'movie' | 'tv') => {\r\n if (!user || !isAuthReady || !watchlistDocRef) return;\r\n\r\n const watchlistItem = { id, type };\r\n try {\r\n await updateDoc(watchlistDocRef, { items: arrayRemove(watchlistItem) });\r\n toast({ title: \"Removed from My List\", description: \"The item has been removed.\" });\r\n } catch (error) {\r\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\r\n console.error(\"Error removing from watchlist:\", error);\r\n toast({ variant: \"destructive\", title: \"Error\", description: errorMessage });\r\n }\r\n }, [user, isAuthReady, watchlistDocRef, toast]);\r\n\r\n const isInWatchlist = useCallback((id: number, type: 'movie' | 'tv') => {\r\n if (!user || !isAuthReady) return false;\r\n return watchlistSet.has(`${type}-${id}`);\r\n }, [user, isAuthReady, watchlistSet]);\r\n\r\n const contextValue = useMemo(() => ({\r\n watchlist,\r\n addToWatchlist,\r\n removeFromWatchlist,\r\n isInWatchlist,\r\n isLoading: isWatchlistLoading\r\n }), [watchlist, addToWatchlist, removeFromWatchlist, isInWatchlist, isWatchlistLoading]);\r\n\r\n return (\r\n <WatchlistContext.Provider value={contextValue}>\r\n {children}\r\n </WatchlistContext.Provider>\r\n );\r\n};\r\n\r\nexport const useWatchlist = () => {\r\n const context = useContext(WatchlistContext);\r\n if (context === undefined) {\r\n throw new Error('useWatchlist must be used within a WatchlistProvider');\r\n }\r\n return context;\r\n};\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\client-provider.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\error-emitter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\errors.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\firestore\\use-collection.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\firebase\\firestore\\use-collection.tsx:44:7\n 42 | useEffect(() => {\n 43 | if (!memoizedTargetRefOrQuery) {\n> 44 | setData(null);\n | ^^^^^^^ Avoid calling setState() directly within an effect\n 45 | setIsLoading(false);\n 46 | setError(null);\n 47 | return;","line":44,"column":7,"nodeType":null,"endLine":44,"endColumn":14}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n\nimport { useState, useEffect } from 'react';\nimport {\n Query,\n onSnapshot,\n DocumentData,\n FirestoreError,\n QuerySnapshot,\n CollectionReference,\n} from 'firebase/firestore';\nimport { errorEmitter } from '@/firebase/error-emitter';\nimport { FirestorePermissionError } from '@/firebase/errors';\n\nexport type WithId<T> = T & { id: string };\n\nexport interface UseCollectionResult<T> {\n data: WithId<T>[] | null;\n isLoading: boolean;\n error: FirestoreError | Error | null;\n}\n\nexport interface InternalQuery extends Query<DocumentData> {\n _query: {\n path: {\n canonicalString(): string;\n toString(): string;\n }\n }\n}\n\nexport function useCollection<T = any>(\n memoizedTargetRefOrQuery: ((CollectionReference<DocumentData> | Query<DocumentData>) & {__memo?: boolean}) | null | undefined,\n): UseCollectionResult<T> {\n type ResultItemType = WithId<T>;\n type StateDataType = ResultItemType[] | null;\n\n const [data, setData] = useState<StateDataType>(null);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<FirestoreError | Error | null>(null);\n\n useEffect(() => {\n if (!memoizedTargetRefOrQuery) {\n setData(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n const unsubscribe = onSnapshot(\n memoizedTargetRefOrQuery,\n (snapshot: QuerySnapshot<DocumentData>) => {\n const results: ResultItemType[] = [];\n for (const doc of snapshot.docs) {\n results.push({ ...(doc.data() as T), id: doc.id });\n }\n setData(results);\n setError(null);\n setIsLoading(false);\n },\n (error: FirestoreError) => {\n const path: string =\n memoizedTargetRefOrQuery.type === 'collection'\n ? (memoizedTargetRefOrQuery as CollectionReference).path\n : (memoizedTargetRefOrQuery as unknown as InternalQuery)._query.path.canonicalString()\n\n const contextualError = new FirestorePermissionError({\n operation: 'list',\n path,\n })\n\n setError(contextualError)\n setData(null)\n setIsLoading(false)\n\n errorEmitter.emit('permission-error', contextualError);\n }\n );\n\n return () => unsubscribe();\n }, [memoizedTargetRefOrQuery]);\n if(memoizedTargetRefOrQuery && !memoizedTargetRefOrQuery.__memo) {\n throw new Error(memoizedTargetRefOrQuery + ' was not properly memoized using useMemoFirebase');\n }\n return { data, isLoading, error };\n}","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\firestore\\use-doc.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\firebase\\firestore\\use-doc.tsx:33:7\n 31 | useEffect(() => {\n 32 | if (!memoizedDocRef) {\n> 33 | setData(null);\n | ^^^^^^^ Avoid calling setState() directly within an effect\n 34 | setIsLoading(false);\n 35 | setError(null);\n 36 | return;","line":33,"column":7,"nodeType":null,"endLine":33,"endColumn":14}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\n \nimport { useState, useEffect } from 'react';\nimport {\n DocumentReference,\n onSnapshot,\n DocumentData,\n FirestoreError,\n DocumentSnapshot,\n} from 'firebase/firestore';\nimport { errorEmitter } from '@/firebase/error-emitter';\nimport { FirestorePermissionError } from '@/firebase/errors';\n\ntype WithId<T> = T & { id: string };\n\nexport interface UseDocResult<T> {\n data: WithId<T> | null;\n isLoading: boolean;\n error: FirestoreError | Error | null;\n}\n\nexport function useDoc<T = any>(\n memoizedDocRef: DocumentReference<DocumentData> | null | undefined,\n): UseDocResult<T> {\n type StateDataType = WithId<T> | null;\n\n const [data, setData] = useState<StateDataType>(null);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<FirestoreError | Error | null>(null);\n\n useEffect(() => {\n if (!memoizedDocRef) {\n setData(null);\n setIsLoading(false);\n setError(null);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n const unsubscribe = onSnapshot(\n memoizedDocRef,\n (snapshot: DocumentSnapshot<DocumentData>) => {\n if (snapshot.exists()) {\n setData({ ...(snapshot.data() as T), id: snapshot.id });\n } else {\n setData(null);\n }\n setError(null);\n setIsLoading(false);\n },\n (error: FirestoreError) => {\n const contextualError = new FirestorePermissionError({\n operation: 'get',\n path: memoizedDocRef.path,\n })\n\n setError(contextualError)\n setData(null)\n setIsLoading(false)\n\n errorEmitter.emit('permission-error', contextualError);\n }\n );\n\n return () => unsubscribe();\n }, [memoizedDocRef]);\n\n return { data, isLoading, error };\n}","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\provider.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\firebase\\provider.tsx:75:7\n 73 | useEffect(() => {\n 74 | if (!auth) {\n> 75 | setUserAuthState({ user: null, isUserLoading: false, userError: new Error(\"Auth service not provided.\"), isAuthReady: true });\n | ^^^^^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 76 | return;\n 77 | }\n 78 |","line":75,"column":7,"nodeType":null,"endLine":75,"endColumn":23},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useMemo received a function whose dependencies are unknown. Pass an inline function instead.","line":191,"column":20,"nodeType":"Identifier","endLine":191,"endColumn":27},{"ruleId":"react-hooks/use-memo","severity":2,"message":"Error: Expected the first argument to be an inline function expression\n\nExpected the first argument to be an inline function expression.\n\nD:\\Visual_Code\\StreamVerse\\src\\firebase\\provider.tsx:191:28\n 189 |\n 190 | export function useMemoFirebase<T>(factory: () => T, deps: DependencyList): T | (MemoFirebase<T>) {\n> 191 | const memoized = useMemo(factory, deps);\n | ^^^^^^^ Expected the first argument to be an inline function expression\n 192 | \n 193 | if(typeof memoized !== 'object' || memoized === null) return memoized;\n 194 | (memoized as MemoFirebase<T>).__memo = true;","line":191,"column":28,"nodeType":null,"endLine":191,"endColumn":35},{"ruleId":"react-hooks/use-memo","severity":2,"message":"Error: Expected the dependency list for useMemo to be an array literal\n\nExpected the dependency list for useMemo to be an array literal.\n\nD:\\Visual_Code\\StreamVerse\\src\\firebase\\provider.tsx:191:37\n 189 |\n 190 | export function useMemoFirebase<T>(factory: () => T, deps: DependencyList): T | (MemoFirebase<T>) {\n> 191 | const memoized = useMemo(factory, deps);\n | ^^^^ Expected the dependency list for useMemo to be an array literal\n 192 | \n 193 | if(typeof memoized !== 'object' || memoized === null) return memoized;\n 194 | (memoized as MemoFirebase<T>).__memo = true;","line":191,"column":37,"nodeType":null,"endLine":191,"endColumn":41}],"suppressedMessages":[],"errorCount":3,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport React, { DependencyList, createContext, useContext, ReactNode, useMemo, useState, useEffect } from 'react';\r\nimport { FirebaseApp } from 'firebase/app';\r\nimport { Firestore, getDoc, setDoc, updateDoc, doc } from 'firebase/firestore';\r\nimport { Auth, User, onAuthStateChanged } from 'firebase/auth';\r\nimport { FirebaseStorage } from 'firebase/storage';\r\nimport { FirebaseErrorListener } from '@/components/FirebaseErrorListener'\r\n\r\ninterface FirebaseProviderProps {\r\n children: ReactNode;\r\n firebaseApp: FirebaseApp;\r\n firestore: Firestore;\r\n auth: Auth;\r\n storage: FirebaseStorage;\r\n}\r\n\r\ninterface UserAuthState {\r\n user: User | null;\r\n isUserLoading: boolean;\r\n userError: Error | null;\r\n isAuthReady: boolean;\r\n}\r\n\r\nexport interface FirebaseContextState {\r\n areServicesAvailable: boolean;\r\n firebaseApp: FirebaseApp | null;\r\n firestore: Firestore | null;\r\n auth: Auth | null;\r\n storage: FirebaseStorage | null;\r\n\r\n user: User | null;\r\n isUserLoading: boolean;\r\n userError: Error | null;\r\n isAuthReady: boolean;\r\n}\r\n\r\nexport interface FirebaseServicesAndUser {\r\n firebaseApp: FirebaseApp;\r\n firestore: Firestore;\r\n auth: Auth;\r\n storage: FirebaseStorage;\r\n user: User | null;\r\n isUserLoading: boolean;\r\n userError: Error | null;\r\n isAuthReady: boolean;\r\n}\r\n\r\nexport interface UserHookResult { \r\n user: User | null;\r\n isUserLoading: boolean;\r\n userError: Error | null;\r\n isAuthReady: boolean;\r\n}\r\n\r\n\r\nexport const FirebaseContext = createContext<FirebaseContextState | undefined>(undefined);\r\n\r\nexport const FirebaseProvider: React.FC<FirebaseProviderProps> = ({\r\n children,\r\n firebaseApp,\r\n firestore,\r\n auth,\r\n storage,\r\n}) => {\r\n const [userAuthState, setUserAuthState] = useState<UserAuthState>({\r\n user: null,\r\n isUserLoading: true,\r\n userError: null,\r\n isAuthReady: false,\r\n });\r\n\r\n useEffect(() => {\r\n if (!auth) {\r\n setUserAuthState({ user: null, isUserLoading: false, userError: new Error(\"Auth service not provided.\"), isAuthReady: true });\r\n return;\r\n }\r\n\r\n if (!firestore) {\r\n setUserAuthState({ user: null, isUserLoading: false, userError: new Error(\"Firestore not provided.\"), isAuthReady: true });\r\n return;\r\n }\r\n\r\n const createUserProfileIfNeeded = async (firebaseUser: User) => {\r\n try {\r\n const userDocRef = doc(firestore, 'users', firebaseUser.uid);\r\n const userDoc = await getDoc(userDocRef);\r\n\r\n if (!userDoc.exists()) {\r\n await setDoc(userDocRef, {\r\n email: firebaseUser.email,\r\n createdAt: Date.now(),\r\n isAdmin: false\r\n }, { merge: true });\r\n\r\n console.log('User profile created for', firebaseUser.email);\r\n } else {\r\n const userData = userDoc.data();\r\n if (userData.email !== firebaseUser.email) {\r\n await updateDoc(userDocRef, {\r\n email: firebaseUser.email\r\n });\r\n }\r\n }\r\n } catch (error) {\r\n console.error('Error creating user profile:', error);\r\n }\r\n };\r\n\r\n const unsubscribe = onAuthStateChanged(\r\n auth,\r\n (firebaseUser) => {\r\n setUserAuthState({ user: firebaseUser, isUserLoading: false, userError: null, isAuthReady: true });\r\n\r\n if (firebaseUser) {\r\n createUserProfileIfNeeded(firebaseUser);\r\n }\r\n },\r\n (error) => {\r\n console.error(\"FirebaseProvider: onAuthStateChanged error:\", error);\r\n setUserAuthState({ user: null, isUserLoading: false, userError: error, isAuthReady: true });\r\n }\r\n );\r\n return () => unsubscribe();\r\n }, [auth, firestore]);\r\n\r\n const contextValue = useMemo((): FirebaseContextState => {\r\n const servicesAvailable = !!(firebaseApp && firestore && auth && storage);\r\n return {\r\n areServicesAvailable: servicesAvailable,\r\n firebaseApp: servicesAvailable ? firebaseApp : null,\r\n firestore: servicesAvailable ? firestore : null,\r\n auth: servicesAvailable ? auth : null,\r\n storage: servicesAvailable ? storage : null,\r\n user: userAuthState.user,\r\n isUserLoading: userAuthState.isUserLoading,\r\n userError: userAuthState.userError,\r\n isAuthReady: userAuthState.isAuthReady,\r\n };\r\n }, [firebaseApp, firestore, auth, storage, userAuthState]);\r\n\r\n return (\r\n <FirebaseContext.Provider value={contextValue}>\r\n <FirebaseErrorListener />\r\n {children}\r\n </FirebaseContext.Provider>\r\n );\r\n};\r\n\r\nexport const useFirebase = (): FirebaseServicesAndUser => {\r\n const context = useContext(FirebaseContext);\r\n\r\n if (context === undefined) {\r\n throw new Error('useFirebase must be used within a FirebaseProvider.');\r\n }\r\n\r\n if (!context.areServicesAvailable || !context.firebaseApp || !context.firestore || !context.auth || !context.storage) {\r\n throw new Error('Firebase core services not available. Check FirebaseProvider props.');\r\n }\r\n\r\n return {\r\n firebaseApp: context.firebaseApp,\r\n firestore: context.firestore,\r\n auth: context.auth,\r\n storage: context.storage,\r\n user: context.user,\r\n isUserLoading: context.isUserLoading,\r\n userError: context.userError,\r\n isAuthReady: context.isAuthReady,\r\n };\r\n};\r\n\r\nexport const useAuth = (): Auth => {\r\n const { auth } = useFirebase();\r\n return auth;\r\n};\r\n\r\nexport const useFirestore = (): Firestore => {\r\n const { firestore } = useFirebase();\r\n return firestore;\r\n};\r\n\r\nexport const useFirebaseApp = (): FirebaseApp => {\r\n const { firebaseApp } = useFirebase();\r\n return firebaseApp;\r\n};\r\n\r\ntype MemoFirebase <T> = T & {__memo?: boolean};\r\n\r\nexport function useMemoFirebase<T>(factory: () => T, deps: DependencyList): T | (MemoFirebase<T>) {\r\n const memoized = useMemo(factory, deps);\r\n \r\n if(typeof memoized !== 'object' || memoized === null) return memoized;\r\n (memoized as MemoFirebase<T>).__memo = true;\r\n \r\n return memoized;\r\n}\r\n\r\nexport const useUser = (): UserHookResult => {\r\n const { user, isUserLoading, userError, isAuthReady } = useFirebase();\r\n return { user, isUserLoading, userError, isAuthReady };\r\n};\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\firebase\\server.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-admin-colors.ts","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\hooks\\use-admin-colors.ts:18:7\n 16 | useEffect(() => {\n 17 | if (!firestore) {\n> 18 | setIsLoading(false);\n | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 19 | return;\n 20 | }\n 21 |","line":18,"column":7,"nodeType":null,"endLine":18,"endColumn":19}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useEffect } from 'react';\nimport { doc, onSnapshot } from 'firebase/firestore';\nimport { useFirebase } from '@/firebase/provider';\nimport { hexToHsl, isValidHex, normalizeHex } from '@/lib/color-utils';\n\nconst DEFAULT_ADMIN_COLOR = '#3b82f6';\nconst DEFAULT_ADMIN_HSL = '217 91% 60%';\n\nexport function useAdminColors() {\n const { firestore } = useFirebase();\n const [adminColor, setAdminColor] = useState<string | null>(null);\n const [adminHsl, setAdminHsl] = useState<string>(DEFAULT_ADMIN_HSL);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (!firestore) {\n setIsLoading(false);\n return;\n }\n\n try {\n const settingsRef = doc(firestore, 'system', 'projectSettings');\n\n const unsubscribe = onSnapshot(\n settingsRef,\n (snapshot) => {\n try {\n if (snapshot.exists()) {\n const data = snapshot.data();\n const color = data?.branding?.primaryColor;\n\n if (color && isValidHex(color)) {\n const normalizedColor = normalizeHex(color);\n const hsl = hexToHsl(normalizedColor);\n\n setAdminColor(normalizedColor);\n setAdminHsl(hsl || DEFAULT_ADMIN_HSL);\n } else {\n setAdminColor(DEFAULT_ADMIN_COLOR);\n setAdminHsl(DEFAULT_ADMIN_HSL);\n }\n } else {\n setAdminColor(DEFAULT_ADMIN_COLOR);\n setAdminHsl(DEFAULT_ADMIN_HSL);\n }\n setError(null);\n } catch (err) {\n console.error('Error processing admin color:', err);\n setAdminColor(DEFAULT_ADMIN_COLOR);\n setAdminHsl(DEFAULT_ADMIN_HSL);\n setError(err instanceof Error ? err : new Error('Unknown error'));\n }\n setIsLoading(false);\n },\n (err) => {\n console.error('Error fetching admin colors:', err);\n setAdminColor(DEFAULT_ADMIN_COLOR);\n setAdminHsl(DEFAULT_ADMIN_HSL);\n setError(err);\n setIsLoading(false);\n }\n );\n\n return () => unsubscribe();\n } catch (err) {\n console.error('Error setting up admin color listener:', err);\n setAdminColor(DEFAULT_ADMIN_COLOR);\n setAdminHsl(DEFAULT_ADMIN_HSL);\n setError(err instanceof Error ? err : new Error('Unknown error'));\n setIsLoading(false);\n }\n }, [firestore]);\n\n return {\n adminColor: adminColor || DEFAULT_ADMIN_COLOR,\n adminHsl,\n isLoading,\n error,\n };\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-admin.ts","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'firestore'. Either include it or remove the dependency array.","line":38,"column":6,"nodeType":"ArrayExpression","endLine":38,"endColumn":12,"suggestions":[{"desc":"Update the dependencies array to be: [firestore, user]","fix":{"range":[1003,1009],"text":"[firestore, user]"}}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useFirebase } from '@/firebase';\nimport { useEffect, useState } from 'react';\nimport { getDoc, doc } from 'firebase/firestore';\n\nexport function useAdmin() {\n const { user, firestore } = useFirebase();\n const [isAdmin, setIsAdmin] = useState(false);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n const checkAdminStatus = async () => {\n if (!user) {\n setIsAdmin(false);\n setIsLoading(false);\n return;\n }\n\n try {\n const db = firestore;\n const userDocRef = doc(db, 'users', user.uid);\n const userDoc = await getDoc(userDocRef);\n\n if (userDoc.exists()) {\n const data = userDoc.data();\n setIsAdmin(data?.isAdmin === true);\n } else {\n setIsAdmin(false);\n }\n } catch (error) {\n console.error('Error checking admin status:', error);\n setIsAdmin(false);\n } finally {\n setIsLoading(false);\n }\n };\n\n checkAdminStatus();\n }, [user]);\n\n return { isAdmin, isLoading };\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-debounce.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-mobile.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-service-worker.tsx","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\hooks\\use-service-worker.tsx:24:9\n 22 |\n 23 | const isSupported = 'serviceWorker' in navigator;\n> 24 | setState((prev) => ({ ...prev, isSupported }));\n | ^^^^^^^^ Avoid calling setState() directly within an effect\n 25 |\n 26 | if (!isSupported) return;\n 27 |","line":24,"column":9,"nodeType":null,"endLine":24,"endColumn":17}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"'use client';\r\n\r\nimport { useEffect, useState } from 'react';\r\n\r\ninterface ServiceWorkerState {\r\n isSupported: boolean;\r\n isRegistered: boolean;\r\n isOnline: boolean;\r\n registration: ServiceWorkerRegistration | null;\r\n}\r\n\r\nexport function useServiceWorker() {\r\n const [state, setState] = useState<ServiceWorkerState>({\r\n isSupported: false,\r\n isRegistered: false,\r\n isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true,\r\n registration: null,\r\n });\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return;\r\n\r\n const isSupported = 'serviceWorker' in navigator;\r\n setState((prev) => ({ ...prev, isSupported }));\r\n\r\n if (!isSupported) return;\r\n\r\n const registerServiceWorker = async () => {\r\n try {\r\n const registration = await navigator.serviceWorker.register('/sw.js', {\r\n scope: '/',\r\n });\r\n\r\n setState((prev) => ({\r\n ...prev,\r\n isRegistered: true,\r\n registration,\r\n }));\r\n\r\n registration.addEventListener('updatefound', () => {\r\n const newWorker = registration.installing;\r\n if (newWorker) {\r\n newWorker.addEventListener('statechange', () => {\r\n if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {\r\n console.log('New content available, refresh to update.');\r\n }\r\n });\r\n }\r\n });\r\n } catch (error) {\r\n console.error('Service worker registration failed:', error);\r\n }\r\n };\r\n\r\n registerServiceWorker();\r\n\r\n const handleOnline = () => setState((prev) => ({ ...prev, isOnline: true }));\r\n const handleOffline = () => setState((prev) => ({ ...prev, isOnline: false }));\r\n\r\n window.addEventListener('online', handleOnline);\r\n window.addEventListener('offline', handleOffline);\r\n\r\n return () => {\r\n window.removeEventListener('online', handleOnline);\r\n window.removeEventListener('offline', handleOffline);\r\n };\r\n }, []);\r\n\r\n return state;\r\n}\r\n\r\ninterface OfflineIndicatorProps {\r\n className?: string;\r\n}\r\n\r\nexport function OfflineIndicator({ className }: OfflineIndicatorProps) {\r\n const { isOnline } = useServiceWorker();\r\n\r\n if (isOnline) return null;\r\n\r\n return (\r\n <div\r\n className={`fixed bottom-4 left-4 right-4 md:left-auto md:right-4 md:w-auto bg-yellow-500 text-yellow-950 px-4 py-2 rounded-lg shadow-lg z-50 flex items-center gap-2 ${className}`}\r\n >\r\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414\"\r\n />\r\n </svg>\r\n <span className=\"text-sm font-medium\">You are offline</span>\r\n </div>\r\n );\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-theme-color.ts","messages":[{"ruleId":"react-hooks/set-state-in-effect","severity":2,"message":"Error: Calling setState synchronously within an effect can trigger cascading renders\n\nEffects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).\n\nD:\\Visual_Code\\StreamVerse\\src\\hooks\\use-theme-color.ts:20:7\n 18 | useEffect(() => {\n 19 | if (!user) {\n> 20 | setIsLoading(false);\n | ^^^^^^^^^^^^ Avoid calling setState() directly within an effect\n 21 | return;\n 22 | }\n 23 |","line":20,"column":7,"nodeType":null,"endLine":20,"endColumn":19}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState, useEffect, useCallback } from 'react';\nimport { useUser } from '@/firebase/provider';\nimport { useUserProfile } from '@/context/user-profile-context';\nimport { applyTheme, DEFAULT_THEME } from '@/lib/theme-config';\nimport { useAdminColors } from '@/hooks/use-admin-colors';\n\nconst STORAGE_KEY = 'user-theme';\nconst ADMIN_DEFAULT_THEME = '__admin_default__';\n\nexport function useThemeColor() {\n const { user } = useUser();\n const { profile, updateProfile, isUpdating } = useUserProfile();\n const { adminHsl } = useAdminColors();\n const [currentTheme, setCurrentTheme] = useState<string>(DEFAULT_THEME);\n const [isLoading, setIsLoading] = useState(true);\n const [usingAdminDefault, setUsingAdminDefault] = useState(false);\n\n useEffect(() => {\n if (!user) {\n setIsLoading(false);\n return;\n }\n\n const loadTheme = async () => {\n setIsLoading(true);\n\n if (profile?.theme) {\n setCurrentTheme(profile.theme);\n applyTheme(profile.theme);\n setUsingAdminDefault(false);\n localStorage.setItem(STORAGE_KEY, profile.theme);\n } else {\n if (adminHsl) {\n if (typeof document !== 'undefined') {\n document.documentElement.style.setProperty('--primary', adminHsl);\n document.documentElement.style.setProperty('--ring', adminHsl);\n }\n setCurrentTheme(ADMIN_DEFAULT_THEME);\n setUsingAdminDefault(true);\n localStorage.removeItem(STORAGE_KEY);\n } else {\n setCurrentTheme(DEFAULT_THEME);\n applyTheme(DEFAULT_THEME);\n setUsingAdminDefault(false);\n localStorage.setItem(STORAGE_KEY, DEFAULT_THEME);\n }\n }\n\n setIsLoading(false);\n };\n\n loadTheme();\n }, [user, profile?.theme, adminHsl]);\n\n const setTheme = useCallback(\n async (themeId: string) => {\n try {\n setCurrentTheme(themeId);\n applyTheme(themeId);\n setUsingAdminDefault(false);\n localStorage.setItem(STORAGE_KEY, themeId);\n\n if (user && profile) {\n await updateProfile({ theme: themeId });\n }\n } catch (error) {\n console.error('Error setting theme:', error);\n setCurrentTheme(currentTheme);\n applyTheme(currentTheme);\n }\n },\n [user, profile, updateProfile, currentTheme]\n );\n\n const resetToAdminDefault = useCallback(\n async () => {\n try {\n if (adminHsl && typeof document !== 'undefined') {\n document.documentElement.style.setProperty('--primary', adminHsl);\n document.documentElement.style.setProperty('--ring', adminHsl);\n }\n setCurrentTheme(ADMIN_DEFAULT_THEME);\n setUsingAdminDefault(true);\n localStorage.removeItem(STORAGE_KEY);\n\n if (user && profile) {\n await updateProfile({ theme: undefined });\n }\n } catch (error) {\n console.error('Error resetting to admin default:', error);\n }\n },\n [user, profile, updateProfile, adminHsl]\n );\n\n return {\n currentTheme,\n setTheme,\n resetToAdminDefault,\n isLoading,\n isUpdating,\n usingAdminDefault,\n };\n}\n","usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\hooks\\use-toast.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\actor-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\cache-service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\color-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\env.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\placeholder-images.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\placeholder-urls.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\system-settings.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\theme-config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\tmdb-cached.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\tmdb-logger.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\tmdb.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\lib\\utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\src\\middleware.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"D:\\Visual_Code\\StreamVerse\\tailwind.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}]