Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AuthProvider } from "./context/AuthContext";
import { BrowserRouter as Router, Routes, Route, useLocation, Navigate } from "react-router-dom";
import Navbar from "./components/Navbar";
import Chatbot from "./components/Chatbot";
import Footer from "./components/Footer";

// Pages
import Landing from "./pages/Landing";
Expand All @@ -25,6 +26,12 @@ import ForumFeed from "./pages/ForumFeed";
import ForumPostDetail from "./pages/ForumPostDetail.jsx";
import ForumPostCreateEdit from "./pages/ForumPostCreateEdit";

// Info pages
import About from "./pages/About";
import Contact from "./pages/Contact";
import Terms from "./pages/Terms";
import Privacy from "./pages/Privacy";

// Placeholder pages
function Placeholder({ title }) {
return <div style={{ padding: 40, textAlign: 'center', color: '#888' }}><h1>{title} (Coming Soon)</h1></div>;
Expand All @@ -51,10 +58,10 @@ export default function App() {
<Route path="/reviews/:id" element={<ReviewDetails />} />
<Route path="/watchlist" element={<Watchlist />} />
<Route path="/ai" element={<AIRecommendations />} />
<Route path="/about" element={<Placeholder title="About" />} />
<Route path="/contact" element={<Placeholder title="Contact" />} />
<Route path="/terms" element={<Placeholder title="Terms & Conditions" />} />
<Route path="/privacy" element={<Placeholder title="Privacy Policy" />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/terms" element={<Terms />} />
<Route path="/privacy" element={<Privacy />} />
<Route path="/discover" element={<Placeholder title="Discover" />} />
<Route path="/trending" element={<Placeholder title="Trending" />} />
<Route path="/top-rated" element={<Placeholder title="Top Rated" />} />
Expand Down Expand Up @@ -85,6 +92,7 @@ export default function App() {
<Route path="/AI Recommendation" element={<Navigate to="/ai" replace />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
<FooterVisibility />
<GlobalChatbotVisibility />
</Router>
</AuthProvider>
Expand All @@ -96,6 +104,13 @@ function NavbarVisibility() {
return <Navbar />;
}

// Show Footer on all pages except login/register
function FooterVisibility() {
const { pathname } = useLocation();
if (pathname === "/login" || pathname === "/register") return null;
return <Footer />;
}

// Hide Chatbot only on login/register
function GlobalChatbotVisibility() {
const { pathname } = useLocation();
Expand Down
118 changes: 118 additions & 0 deletions frontend/src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { FaFacebook, FaLinkedin, FaGithub, FaHeart } from 'react-icons/fa';

export default function Footer() {
const currentYear = new Date().getFullYear();
const [gotQuote, setGotQuote] = useState(null);

useEffect(() => {
fetch('https://api.gameofthronesquotes.xyz/v1/random')
.then(res => res.json())
.then(data => {
if (Array.isArray(data) && data.length > 0) {
setGotQuote(data[0]);
} else if (data.sentence) {
setGotQuote(data);
}
})
.catch(() => setGotQuote(null));
}, []);

return (
<footer className="bg-gray-900 dark:bg-black border-t border-gray-800 dark:border-gray-700 mt-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* Brand Section */}
<div>
<Link to="/" className="flex items-center mb-4">
<span className="text-2xl font-bold bg-gradient-to-r from-red-500 to-red-700 text-transparent bg-clip-text">
RedPill
</span>
</Link>
<p className="text-gray-400 text-sm max-w-md">
Discover, review, and discuss movies with fellow film enthusiasts.
Get AI-powered recommendations and analyze movie personalities in our community.
</p>
<div className="flex items-center mt-4 text-gray-400 text-sm">
<span>Made with</span>
<FaHeart className="text-red-500 mx-1" />
<span>by movie lovers</span>
</div>

{/* Game of Thrones Quote */}
{gotQuote && (
<div className="mt-6 text-left">
<div className="italic text-base text-gray-300">
<span>❝{gotQuote.sentence}❞</span>
{gotQuote.character && (
<span className="block mt-2 text-sm text-gray-500">— {gotQuote.character.name}</span>
)}
</div>
</div>
)}
</div>

{/* Legal & Info */}
<div>
<h3 className="text-white font-semibold mb-4">Information</h3>
<ul className="space-y-2">
<li>
<Link
to="/about"
className="text-gray-400 hover:text-white transition-colors text-sm"
>
About Us
</Link>
</li>
<li>
<Link
to="/contact"
className="text-gray-400 hover:text-white transition-colors text-sm"
>
Contact
</Link>
</li>
<li>
<Link
to="/terms"
className="text-gray-400 hover:text-white transition-colors text-sm"
>
Terms of Service
</Link>
</li>
<li>
<Link
to="/privacy"
className="text-gray-400 hover:text-white transition-colors text-sm"
>
Privacy Policy
</Link>
</li>
</ul>
</div>
</div>

{/* Divider */}
<div className="border-t border-gray-800 dark:border-gray-700 mt-8 pt-8">
<div className="flex flex-col md:flex-row justify-between items-center">
<p className="text-gray-400 text-sm">
© {currentYear} RedPill. All rights reserved.
</p>
<div className="flex items-center space-x-4 mt-4 md:mt-0">
<span className="text-gray-400 text-sm">Follow us:</span>
<a
href="https://github.com/RedPill-Team"
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-white transition-colors"
>
<FaGithub className="w-5 h-5" />
</a>
</div>
</div>
</div>
</div>
</footer>
);
}
136 changes: 136 additions & 0 deletions frontend/src/components/UserProfileAnalysis.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import React from 'react';
import { motion } from 'framer-motion';

export default function UserProfileAnalysis({ analysis, onClose }) {
if (!analysis) return null;

const getPersonalityIcon = (type) => {
switch (type.toLowerCase()) {
case 'movie enthusiast':
case 'movie buff':
return '🎬';
case 'casual viewer':
return '🍿';
case 'genre specialist':
return '🎭';
default:
return '🎥';
}
};

const getCompatibilityColor = (score) => {
if (score >= 80) return 'text-green-400';
if (score >= 60) return 'text-yellow-400';
return 'text-red-400';
};

const getCompatibilityText = (score) => {
if (score >= 80) return 'Highly Compatible';
if (score >= 60) return 'Moderately Compatible';
return 'Different Tastes';
};

return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-white/10 mb-8"
>
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-bold bg-gradient-to-r from-purple-400 to-blue-400 text-transparent bg-clip-text">
🎭 Movie Personality Analysis
</h3>
<div className="flex items-center gap-3">
<div className="text-2xl">{getPersonalityIcon(analysis.personality_type)}</div>
<button
onClick={onClose}
className="p-2 rounded-full hover:bg-white/10 transition-colors text-gray-400 hover:text-white"
>
</button>
</div>
</div>

{/* Personality Type */}
<div className="mb-6">
<div className="flex items-center justify-between mb-2">
<span className="text-gray-300 font-medium">Personality Type</span>
<span className="text-white font-bold">{analysis.personality_type}</span>
</div>
<p className="text-gray-400 text-sm">{analysis.summary}</p>
</div>

{/* Compatibility Score */}
<div className="mb-6 p-4 bg-white/5 rounded-lg">
<div className="flex items-center justify-between mb-2">
<span className="text-gray-300 font-medium">Movie Compatibility</span>
<span className={`font-bold text-lg ${getCompatibilityColor(analysis.compatibility_score)}`}>
{analysis.compatibility_score}%
</span>
</div>
<div className="w-full bg-gray-700 rounded-full h-2 mb-2">
<div
className={`h-2 rounded-full transition-all duration-500 ${
analysis.compatibility_score >= 80 ? 'bg-green-500' :
analysis.compatibility_score >= 60 ? 'bg-yellow-500' : 'bg-red-500'
}`}
style={{ width: `${analysis.compatibility_score}%` }}
/>
</div>
<p className={`text-sm ${getCompatibilityColor(analysis.compatibility_score)}`}>
{getCompatibilityText(analysis.compatibility_score)}
</p>
</div>

{/* Favorite Genres */}
<div className="mb-6">
<h4 className="text-gray-300 font-medium mb-3">Favorite Genres</h4>
<div className="flex flex-wrap gap-2">
{analysis.favorite_genres.map((genre, index) => (
<span
key={index}
className="px-3 py-1 bg-purple-600/20 text-purple-300 rounded-full text-sm border border-purple-500/30"
>
{genre}
</span>
))}
</div>
</div>

{/* Personality Traits */}
<div className="mb-6">
<h4 className="text-gray-300 font-medium mb-3">Personality Traits</h4>
<div className="flex flex-wrap gap-2">
{analysis.personality_traits.map((trait, index) => (
<span
key={index}
className="px-3 py-1 bg-blue-600/20 text-blue-300 rounded-full text-sm border border-blue-500/30"
>
{trait}
</span>
))}
</div>
</div>

{/* Viewing Patterns */}
<div className="mb-6">
<h4 className="text-gray-300 font-medium mb-2">Viewing Patterns</h4>
<p className="text-gray-400 text-sm">{analysis.viewing_patterns}</p>
</div>

{/* Taste Analysis */}
<div className="mb-6">
<h4 className="text-gray-300 font-medium mb-2">Taste Analysis</h4>
<p className="text-gray-400 text-sm">{analysis.taste_analysis}</p>
</div>

{/* Recommendations */}
<div className="p-4 bg-gradient-to-r from-purple-600/10 to-blue-600/10 rounded-lg border border-purple-500/20">
<h4 className="text-purple-300 font-medium mb-2">Movie Recommendations</h4>
<p className="text-gray-300 text-sm">{analysis.recommendations}</p>
</div>
</motion.div>
);
}
Loading