mmrwinston001 commited on
Commit
6ce679b
·
verified ·
1 Parent(s): 83cc215

Upload 32 files (#3)

Browse files

- Upload 32 files (90e82adab5a6d49ad8615502ab60fe4f7f833025)

src/App.tsx ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/App.tsx
2
+ import React, { useEffect, useState } from 'react';
3
+ import { BrowserRouter as Router, Routes, Route, useNavigate } from 'react-router-dom';
4
+ import { ThemeProvider } from './contexts/ThemeContext';
5
+ import { auth, db } from './firebase';
6
+ import { deleteUser } from 'firebase/auth';
7
+ import { doc, deleteDoc, getDoc } from 'firebase/firestore';
8
+ import { Helmet, HelmetProvider } from 'react-helmet-async';
9
+
10
+ // Pages
11
+ import ProfilePage from './pages/ProfilePage';
12
+ import HomePage from './pages/HomePage';
13
+ import AboutPage from './pages/AboutPage';
14
+ import ContactPage from './pages/ContactPage';
15
+ import BlogPage from './pages/BlogPage';
16
+ import GPTChecker from './pages/GPTChecker';
17
+ import WordCounterPage from './pages/WordCounterPage';
18
+ import LoginPage from './pages/LoginPage';
19
+ import SignupPage from './pages/SignupPage';
20
+ import ThankYouPage from './pages/ThankYouPage';
21
+ import WelcomePage from './pages/WelcomePage';
22
+ import PrivacyPolicyPage from "./pages/PrivacyPolicyPage";
23
+ import Terms from "./pages/Terms";
24
+ import DisclaimerPage from "./pages/DisclaimerPage";
25
+ import HelpCenterPage from './pages/HelpCenterPage';
26
+
27
+ // Components
28
+ import AnalyticsCharts from './components/AnalyticsCharts';
29
+ import ActivityFeed from './components/ActivityFeed';
30
+ import SettingsPanel from './components/SettingsPanel';
31
+ import StatsGrid from './components/StatsGrid';
32
+ import ProfileHeader from './components/ProfileHeader';
33
+
34
+ // --- Apply favicon globally ---
35
+ const updateFavicon = () => {
36
+ const faviconUrl = "https://ucarecdn.com/94d22aaf-e546-48b6-b110-5fc2cad94e0c/-/format/auto/";
37
+ document.querySelectorAll('link[rel*="icon"]').forEach(f => f.remove());
38
+ const link = document.createElement('link');
39
+ link.rel = 'icon';
40
+ link.type = 'image/png';
41
+ link.href = faviconUrl;
42
+ link.sizes = '256x256';
43
+ document.head.appendChild(link);
44
+ };
45
+
46
+ // Wrapper to provide user and navigation for SettingsPanel
47
+ const SettingsRouteWrapper: React.FC = () => {
48
+ const [userData, setUserData] = useState<any>(null);
49
+ const navigate = useNavigate();
50
+
51
+ useEffect(() => {
52
+ const fetchUser = async () => {
53
+ const user = auth.currentUser;
54
+ if (user) {
55
+ const docRef = doc(db, 'users', user.uid);
56
+ const docSnap = await getDoc(docRef);
57
+ setUserData({
58
+ email: user.email,
59
+ username: docSnap.exists() ? docSnap.data().username : 'unknown',
60
+ joinDate: docSnap.exists() ? docSnap.data().joinDate : 'Unknown',
61
+ preferences: docSnap.exists() ? docSnap.data().preferences : {},
62
+ });
63
+ }
64
+ };
65
+ fetchUser();
66
+ }, []);
67
+
68
+ const handleSignOut = async () => {
69
+ await auth.signOut();
70
+ navigate('/login');
71
+ };
72
+
73
+ const handleDeleteAccount = async () => {
74
+ if (!auth.currentUser) return;
75
+ const uid = auth.currentUser.uid;
76
+ await deleteDoc(doc(db, 'users', uid));
77
+ await deleteUser(auth.currentUser);
78
+ navigate('/signup');
79
+ };
80
+
81
+ const handleUpdatePreferences = async (prefs: any) => {
82
+ if (!auth.currentUser) return;
83
+ console.log('Updated preferences:', prefs);
84
+ };
85
+
86
+ return (
87
+ <SettingsPanel
88
+ user={userData}
89
+ onSignOut={handleSignOut}
90
+ onDeleteAccount={handleDeleteAccount}
91
+ onUpdatePreferences={handleUpdatePreferences}
92
+ />
93
+ );
94
+ };
95
+
96
+ const App: React.FC = () => {
97
+ useEffect(() => {
98
+ updateFavicon();
99
+ }, []);
100
+
101
+ return (
102
+ <ThemeProvider>
103
+ <HelmetProvider>
104
+ <Router>
105
+ {/* 👇 Global default SEO (will be overridden per page) */}
106
+ <Helmet>
107
+ <title>HumanizerX – Natural, Human-Like AI Text Made Easy</title>
108
+ <meta
109
+ name="description"
110
+ content="HumanizerX transforms AI-generated content into natural, human-like text. Detect GPT content, enhance readability, and make your writing authentic."
111
+ />
112
+ <link rel="canonical" href="https://www.humanizerx.pro/" />
113
+
114
+ {/* ✅ Structured Data: Organization + Logo for Google */}
115
+ <script type="application/ld+json">
116
+ {`
117
+ {
118
+ "@context": "https://schema.org",
119
+ "@type": "Organization",
120
+ "name": "Humanizer X",
121
+ "url": "https://www.humanizerx.pro",
122
+ "logo": "https://ucarecdn.com/94d22aaf-e546-48b6-b110-5fc2cad94e0c/-/format/auto/"
123
+ }
124
+ `}
125
+ </script>
126
+ </Helmet>
127
+
128
+ <main className="min-h-[calc(100vh-4rem)]">
129
+ <Routes>
130
+ {/* Main pages */}
131
+ <Route path="/" element={<HomePage />} />
132
+ <Route path="/gpt-checker" element={<GPTChecker />} />
133
+ <Route path="/word-counter" element={<WordCounterPage />} />
134
+ <Route path="/about" element={<AboutPage />} />
135
+ <Route path="/contact" element={<ContactPage />} />
136
+ <Route path="/blogs" element={<BlogPage />} />
137
+ <Route path="/profile" element={<ProfilePage />} />
138
+ <Route path="/login" element={<LoginPage />} />
139
+ <Route path="/signup" element={<SignupPage />} />
140
+ <Route path="/thank-you" element={<ThankYouPage />} />
141
+ <Route path="/welcome" element={<WelcomePage />} />
142
+ <Route path="/privacy-policy" element={<PrivacyPolicyPage />} />
143
+ <Route path="/terms" element={<Terms />} />
144
+ <Route path="/disclaimer" element={<DisclaimerPage />} />
145
+ {/* Demo routes */}
146
+ <Route path="/analytics" element={<AnalyticsCharts />} />
147
+ <Route path="/activity" element={<ActivityFeed />} />
148
+ <Route path="/settings" element={<SettingsRouteWrapper />} />
149
+ <Route path="/stats" element={<StatsGrid />} />
150
+ <Route path="/profile-header" element={<ProfileHeader />} />
151
+ <Route path="/help-center" element={<HelpCenterPage />} />
152
+ </Routes>
153
+ </main>
154
+ </Router>
155
+ </HelmetProvider>
156
+ </ThemeProvider>
157
+ );
158
+ };
159
+
160
+ export default App;
src/components/ActivityFeed.tsx ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useContext } from 'react';
2
+ import { Clock, User, Settings, Shield, Database } from 'lucide-react';
3
+ import { UserActivity } from '../types/user';
4
+ import { ThemeContext } from '../contexts/ThemeContext';
5
+
6
+ interface ActivityFeedProps {
7
+ activities: UserActivity[];
8
+ }
9
+
10
+ const ActivityFeed: React.FC<ActivityFeedProps> = ({ activities }) => {
11
+ const { theme } = useContext(ThemeContext); // ✅ Use ThemeContext
12
+
13
+ const getActivityIcon = (type: string) => {
14
+ switch (type) {
15
+ case 'login':
16
+ case 'profile':
17
+ return User;
18
+ case 'settings':
19
+ return Settings;
20
+ case 'security':
21
+ return Shield;
22
+ case 'data':
23
+ return Database;
24
+ default:
25
+ return Clock;
26
+ }
27
+ };
28
+
29
+ const getActivityColor = (type: string) => {
30
+ if (theme === 'dark') {
31
+ switch (type) {
32
+ case 'login': return 'text-green-400 bg-green-900';
33
+ case 'profile': return 'text-blue-400 bg-blue-900';
34
+ case 'settings': return 'text-gray-300 bg-gray-800';
35
+ case 'security': return 'text-red-400 bg-red-900';
36
+ case 'data': return 'text-purple-400 bg-purple-900';
37
+ default: return 'text-gray-300 bg-gray-800';
38
+ }
39
+ } else {
40
+ switch (type) {
41
+ case 'login': return 'text-green-600 bg-green-50';
42
+ case 'profile': return 'text-blue-600 bg-blue-50';
43
+ case 'settings': return 'text-gray-600 bg-gray-50';
44
+ case 'security': return 'text-red-600 bg-red-50';
45
+ case 'data': return 'text-purple-600 bg-purple-50';
46
+ default: return 'text-gray-600 bg-gray-50';
47
+ }
48
+ }
49
+ };
50
+
51
+ return (
52
+ <div className={`rounded-xl border p-6 ${
53
+ theme === 'dark'
54
+ ? 'bg-gray-900 border-gray-700 text-white'
55
+ : 'bg-white border-gray-200 text-gray-900'
56
+ }`}>
57
+ <h3 className={`text-lg font-semibold mb-6 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
58
+ Recent Activity
59
+ </h3>
60
+
61
+ {activities.length === 0 ? (
62
+ <div className="text-center py-8">
63
+ <Clock className={`w-12 h-12 mx-auto mb-3 ${theme === 'dark' ? 'text-gray-500' : 'text-gray-300'}`} />
64
+ <p className={theme === 'dark' ? 'text-gray-400' : 'text-gray-500'}>No recent activity</p>
65
+ </div>
66
+ ) : (
67
+ <div className="space-y-4">
68
+ {activities.slice(0, 10).map((activity) => {
69
+ const Icon = getActivityIcon(activity.type);
70
+ return (
71
+ <div
72
+ key={activity.id}
73
+ className={`flex items-start gap-4 p-4 rounded-lg transition ${
74
+ theme === 'dark' ? 'hover:bg-gray-800' : 'hover:bg-gray-50'
75
+ }`}
76
+ >
77
+ <div className={`p-2 rounded-lg ${getActivityColor(activity.type)}`}>
78
+ <Icon className="w-4 h-4" />
79
+ </div>
80
+ <div className="flex-1 min-w-0">
81
+ <p className={theme === 'dark' ? 'text-white' : 'text-gray-900'}>{activity.action}</p>
82
+ {activity.details && (
83
+ <p className={theme === 'dark' ? 'text-gray-400' : 'text-gray-600'}>{activity.details}</p>
84
+ )}
85
+ <p className={theme === 'dark' ? 'text-gray-500' : 'text-gray-500'}>{activity.timestamp}</p>
86
+ </div>
87
+ </div>
88
+ );
89
+ })}
90
+ </div>
91
+ )}
92
+ </div>
93
+ );
94
+ };
95
+
96
+ export default ActivityFeed;
src/components/AnalyticsCharts.tsx ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/components/AnalyticsCharts.tsx
2
+ import React, { useContext } from 'react';
3
+ import {
4
+ Chart as ChartJS,
5
+ CategoryScale,
6
+ LinearScale,
7
+ BarElement,
8
+ LineElement,
9
+ PointElement,
10
+ Title,
11
+ Tooltip,
12
+ Legend,
13
+ ArcElement
14
+ } from 'chart.js';
15
+ import { Bar, Line, Doughnut } from 'react-chartjs-2';
16
+ import { ThemeContext } from '../contexts/ThemeContext';
17
+ import { useUserStats } from '../hooks/useUserStats';
18
+
19
+ ChartJS.register(
20
+ CategoryScale,
21
+ LinearScale,
22
+ BarElement,
23
+ LineElement,
24
+ PointElement,
25
+ Title,
26
+ Tooltip,
27
+ Legend,
28
+ ArcElement
29
+ );
30
+
31
+ const AnalyticsCharts: React.FC = () => {
32
+ const { theme } = useContext(ThemeContext);
33
+ const { stats } = useUserStats();
34
+ const isDark = theme === 'dark';
35
+
36
+ if (!stats) return <p className="text-center py-6">Loading stats...</p>;
37
+
38
+ // ---------- Safe stats values ----------
39
+ const textsHumanized = stats?.textsHumanized ?? 0;
40
+ const wordsProcessed = stats?.wordsProcessed ?? 0;
41
+ const totalSessions = stats?.totalSessions ?? 0;
42
+ const gptAverage = stats?.gptAverage ?? 0;
43
+ const averageSessionTime = stats?.averageSessionTime ?? 0;
44
+
45
+ // ---------- Bar Chart ----------
46
+ const barData = {
47
+ labels: ['Texts Humanized', 'Words Processed (k)', 'Total Sessions'],
48
+ datasets: [
49
+ {
50
+ label: 'User Activity',
51
+ data: [
52
+ textsHumanized,
53
+ Math.round(wordsProcessed / 1000), // scale words for display
54
+ totalSessions
55
+ ],
56
+ backgroundColor: ['rgba(59, 130, 246, 0.8)', 'rgba(16, 185, 129, 0.8)', 'rgba(234, 179, 8, 0.8)'],
57
+ borderColor: ['rgb(59, 130, 246)', 'rgb(16, 185, 129)', 'rgb(234, 179, 8)'],
58
+ borderWidth: 1
59
+ }
60
+ ]
61
+ };
62
+
63
+ // ---------- Line Chart ----------
64
+ const lineData = {
65
+ labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
66
+ datasets: [
67
+ {
68
+ label: 'Texts Humanized Over Weeks',
69
+ data: [
70
+ Math.round(textsHumanized / 4),
71
+ Math.round(textsHumanized / 4) * 2,
72
+ Math.round(textsHumanized / 4) * 3,
73
+ textsHumanized
74
+ ],
75
+ borderColor: 'rgb(59, 130, 246)',
76
+ backgroundColor: 'rgba(59, 130, 246, 0.2)',
77
+ tension: 0.3,
78
+ fill: true,
79
+ pointBackgroundColor: 'rgb(59, 130, 246)'
80
+ },
81
+ {
82
+ label: 'Total Sessions Over Weeks',
83
+ data: [
84
+ Math.round(totalSessions / 4),
85
+ Math.round(totalSessions / 4) * 2,
86
+ Math.round(totalSessions / 4) * 3,
87
+ totalSessions
88
+ ],
89
+ borderColor: 'rgb(234, 179, 8)',
90
+ backgroundColor: 'rgba(234, 179, 8, 0.2)',
91
+ tension: 0.3,
92
+ fill: true,
93
+ pointBackgroundColor: 'rgb(234, 179, 8)'
94
+ }
95
+ ]
96
+ };
97
+
98
+ // ---------- Doughnut Chart ----------
99
+ const doughnutData = {
100
+ labels: ['Texts Humanized', 'Words Processed (k)', 'Total Sessions'],
101
+ datasets: [
102
+ {
103
+ data: [textsHumanized, Math.round(wordsProcessed / 1000), totalSessions],
104
+ backgroundColor: ['rgba(59, 130, 246, 0.8)', 'rgba(16, 185, 129, 0.8)', 'rgba(234, 179, 8, 0.8)'],
105
+ borderColor: ['rgb(59, 130, 246)', 'rgb(16, 185, 129)', 'rgb(234, 179, 8)'],
106
+ borderWidth: 1
107
+ }
108
+ ]
109
+ };
110
+
111
+ // ---------- Chart Options ----------
112
+ const chartOptions = {
113
+ responsive: true,
114
+ maintainAspectRatio: false,
115
+ plugins: {
116
+ legend: { position: 'top' as const, labels: { color: isDark ? '#fff' : '#111' } },
117
+ tooltip: { enabled: true },
118
+ title: { display: false }
119
+ },
120
+ scales: {
121
+ x: {
122
+ ticks: { color: isDark ? '#fff' : '#111' },
123
+ grid: { color: isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)' }
124
+ },
125
+ y: {
126
+ beginAtZero: true,
127
+ ticks: { color: isDark ? '#fff' : '#111' },
128
+ grid: { color: isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)' }
129
+ }
130
+ }
131
+ };
132
+
133
+ const cardClass = `rounded-xl border p-6 ${
134
+ isDark ? 'bg-gray-900 border-gray-700 text-white' : 'bg-white border-gray-200 text-gray-900'
135
+ }`;
136
+ const headingClass = `text-lg font-semibold mb-4 ${isDark ? 'text-white' : 'text-gray-900'}`;
137
+
138
+ return (
139
+ <div className="space-y-6">
140
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
141
+ <div className={cardClass}>
142
+ <h3 className={headingClass}>Activity Overview</h3>
143
+ <div className="h-64">
144
+ <Bar data={barData} options={chartOptions} />
145
+ </div>
146
+ </div>
147
+
148
+ <div className={cardClass}>
149
+ <h3 className={headingClass}>Task Distribution</h3>
150
+ <div className="h-64">
151
+ <Doughnut
152
+ data={doughnutData}
153
+ options={{
154
+ ...chartOptions,
155
+ scales: {} // Doughnut ignores scales
156
+ }}
157
+ />
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <div className={cardClass}>
163
+ <h3 className={headingClass}>Monthly Trends</h3>
164
+ <div className="h-80">
165
+ <Line data={lineData} options={chartOptions} />
166
+ </div>
167
+ </div>
168
+ </div>
169
+ );
170
+ };
171
+
172
+ export default AnalyticsCharts;
src/components/Footer.tsx ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { Link } from 'react-router-dom';
3
+ import { Sparkles, Twitter, Mail, Linkedin } from 'lucide-react';
4
+
5
+ const Footer: React.FC = () => {
6
+ return (
7
+ <footer className="bg-gray-900 dark:bg-gray-950 text-white py-12">
8
+ <div className="container mx-auto px-4">
9
+ <div className="grid md:grid-cols-4 gap-8">
10
+ {/* Brand */}
11
+ <div className="col-span-2 md:col-span-1">
12
+ <Link to="/" className="flex items-center gap-2 text-xl font-bold mb-4">
13
+ <img
14
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
15
+ alt="Humanizer X Footer Logo - AI Text Humanization Tool"
16
+ title="Humanizer X - Advanced AI text humanization and detection tools"
17
+ className="w-10 h-10 object-contain"
18
+ />
19
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
20
+ Humanizer X
21
+ </span>
22
+ </Link>
23
+ <p className="text-gray-400 text-sm leading-relaxed">
24
+ Advanced AI text humanization and detection tools for content creators,
25
+ students, and professionals worldwide.
26
+ </p>
27
+ </div>
28
+
29
+ {/* Quick Links */}
30
+ <div>
31
+ <h3 className="font-semibold mb-4">Tools</h3>
32
+ <ul className="space-y-2">
33
+ <li>
34
+ <Link
35
+ to="/"
36
+ className="text-gray-400 hover:text-white transition-colors text-sm"
37
+ >
38
+ Text Humanizer
39
+ </Link>
40
+ </li>
41
+ <li>
42
+ <Link
43
+ to="/gpt-checker"
44
+ className="text-gray-400 hover:text-white transition-colors text-sm"
45
+ >
46
+ GPT Checker
47
+ </Link>
48
+ </li>
49
+ <li>
50
+ <Link
51
+ to="/word-counter"
52
+ className="text-gray-400 hover:text-white transition-colors text-sm"
53
+ >
54
+ Word Counter
55
+ </Link>
56
+ </li>
57
+ </ul>
58
+ </div>
59
+
60
+ {/* Company */}
61
+ <div>
62
+ <h3 className="font-semibold mb-4">Company</h3>
63
+ <ul className="space-y-2">
64
+ <li>
65
+ <Link
66
+ to="/about"
67
+ className="text-gray-400 hover:text-white transition-colors text-sm"
68
+ >
69
+ About Us
70
+ </Link>
71
+ </li>
72
+ <li>
73
+ <Link
74
+ to="/contact"
75
+ className="text-gray-400 hover:text-white transition-colors text-sm"
76
+ >
77
+ Contact
78
+ </Link>
79
+ </li>
80
+ <li>
81
+ <Link
82
+ to="/privacy-policy"
83
+ className="text-gray-400 hover:text-white transition-colors text-sm"
84
+ >
85
+ Privacy Policy
86
+ </Link>
87
+ </li>
88
+ <li>
89
+ <Link
90
+ to="/terms"
91
+ className="text-gray-400 hover:text-white transition-colors text-sm"
92
+ >
93
+ Terms & Conditions
94
+ </Link>
95
+ </li>
96
+ <li>
97
+ <Link
98
+ to="/disclaimer"
99
+ className="text-gray-400 hover:text-white transition-colors text-sm"
100
+ >
101
+ Disclaimer
102
+ </Link>
103
+ </li>
104
+ </ul>
105
+ </div>
106
+
107
+ {/* Social */}
108
+ <div>
109
+ <h3 className="font-semibold mb-4">Connect</h3>
110
+ <div className="flex gap-3">
111
+ <a
112
+ href="https://x.com/HumanizerX"
113
+ target="_blank"
114
+ rel="noopener noreferrer"
115
+ aria-label="Visit our Twitter profile"
116
+ className="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-gray-700 transition-colors"
117
+ >
118
+ <Twitter size={16} />
119
+ </a>
120
+
121
+ <a
122
+ href="https://mail.google.com/mail/?view=cm&fs=1&to=humanizerx@gmail.com"
123
+ target="_blank"
124
+ rel="noopener noreferrer"
125
+ aria-label="Send us an email"
126
+ className="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-gray-700 transition-colors"
127
+ >
128
+ <Mail size={16} />
129
+ </a>
130
+
131
+ <a
132
+ href="https://www.linkedin.com/in/humanizer-x/"
133
+ target="_blank"
134
+ rel="noopener noreferrer"
135
+ aria-label="Visit our LinkedIn profile"
136
+ className="w-8 h-8 bg-gray-800 rounded-lg flex items-center justify-center hover:bg-gray-700 transition-colors"
137
+ >
138
+ <Linkedin size={16} />
139
+ </a>
140
+
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <div className="border-t border-gray-800 pt-8 mt-8">
146
+
147
+ <div className="flex flex-col md:flex-row justify-between items-center">
148
+ <p className="text-gray-400 text-sm">
149
+ © 2025 Humanizer X. All rights reserved.
150
+ </p>
151
+ <p className="text-gray-400 text-sm mt-2 md:mt-0">
152
+ Built with advanced AI detection and humanization technology
153
+ </p>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </footer>
158
+ );
159
+ };
160
+
161
+ export default Footer;
src/components/Header.tsx ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { Link, useLocation } from 'react-router-dom';
3
+ import { Menu, X, Sun, Moon, LogOut, User as UserIcon } from 'lucide-react';
4
+ import { auth } from '../firebase';
5
+ import { onAuthStateChanged, User as FirebaseUser, signOut } from 'firebase/auth';
6
+
7
+ const Header: React.FC = () => {
8
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
9
+ const [darkMode, setDarkMode] = useState(false);
10
+ const [user, setUser] = useState<FirebaseUser | null>(null);
11
+ const [profilePic, setProfilePic] = useState<string | null>(null);
12
+ const [isProfileOpen, setIsProfileOpen] = useState(false);
13
+ const [showSignOutConfirm, setShowSignOutConfirm] = useState(false); // NEW
14
+ const profileRef = useRef<HTMLDivElement>(null);
15
+ const location = useLocation();
16
+
17
+ const navLinks = [
18
+ { path: '/', label: 'Humanizer X' },
19
+ { path: '/gpt-checker', label: 'GPT Checker' },
20
+ { path: '/word-counter', label: 'Word Counter' }, // <-- added
21
+ { path: '/about', label: 'About' },
22
+ { path: '/contact', label: 'Contact' },
23
+ { path: '/blogs', label: 'Blog' },
24
+ ];
25
+
26
+ // Theme handling
27
+ useEffect(() => {
28
+ const savedTheme = localStorage.getItem('theme');
29
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
30
+ if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
31
+ setDarkMode(true);
32
+ document.documentElement.classList.add('dark');
33
+ } else {
34
+ setDarkMode(false);
35
+ document.documentElement.classList.remove('dark');
36
+ }
37
+ }, []);
38
+
39
+ const toggleDarkMode = () => {
40
+ const newDark = !darkMode;
41
+ setDarkMode(newDark);
42
+ if (newDark) {
43
+ document.documentElement.classList.add('dark');
44
+ localStorage.setItem('theme', 'dark');
45
+ } else {
46
+ document.documentElement.classList.remove('dark');
47
+ localStorage.setItem('theme', 'light');
48
+ }
49
+ };
50
+
51
+ // Auth listener
52
+ useEffect(() => {
53
+ const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
54
+ setUser(currentUser);
55
+ setProfilePic(currentUser?.photoURL || null);
56
+ });
57
+ return () => unsubscribe();
58
+ }, []);
59
+
60
+ // Close profile dropdown when clicking outside
61
+ useEffect(() => {
62
+ const handleClickOutside = (e: MouseEvent) => {
63
+ if (profileRef.current && !profileRef.current.contains(e.target as Node)) {
64
+ setIsProfileOpen(false);
65
+ }
66
+ };
67
+ document.addEventListener('mousedown', handleClickOutside);
68
+ return () => document.removeEventListener('mousedown', handleClickOutside);
69
+ }, []);
70
+
71
+ const isActive = (path: string) =>
72
+ location.pathname === path || location.pathname.startsWith(path + "/");
73
+
74
+ // Sign out function
75
+ const handleSignOut = async () => {
76
+ try {
77
+ await signOut(auth);
78
+ setIsProfileOpen(false);
79
+ setIsMenuOpen(false);
80
+ setShowSignOutConfirm(false); // Close modal
81
+ } catch (err) {
82
+ console.error('Sign out error:', err);
83
+ }
84
+ };
85
+
86
+ return (
87
+ <header className="bg-gray-900 dark:bg-gray-950 text-white py-2 shadow-lg transition-colors duration-300">
88
+ <div className="container mx-auto px-4">
89
+ <div className="flex items-center justify-between h-16">
90
+ {/* Logo */}
91
+ <Link to="/" className="flex items-center gap-2">
92
+ <img
93
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
94
+ alt="Humanizer X - AI Text Humanization Platform Logo"
95
+ title="Humanizer X - Navigate to homepage"
96
+ className="w-10 h-10 object-contain"
97
+ />
98
+ <span className="text-2xl font-bold bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
99
+ Humanizer X
100
+ </span>
101
+ </Link>
102
+
103
+ {/* Desktop Navigation */}
104
+ <nav className="hidden md:flex items-center space-x-8">
105
+ {navLinks.map((link) => (
106
+ <Link
107
+ key={link.path}
108
+ to={link.path}
109
+ className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
110
+ isActive(link.path)
111
+ ? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20'
112
+ : 'text-gray-300 hover:text-blue-600 dark:hover:text-blue-400'
113
+ }`}
114
+ >
115
+ {link.label}
116
+ </Link>
117
+ ))}
118
+ </nav>
119
+
120
+ {/* Right side controls */}
121
+ <div className="flex items-center gap-4 relative">
122
+ {/* Profile/Auth Section */}
123
+ {!user ? (
124
+ <div className="hidden md:flex items-center gap-3">
125
+ <Link
126
+ to="/login"
127
+ className="px-4 py-2 text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
128
+ >
129
+ Sign In
130
+ </Link>
131
+ <Link
132
+ to="/signup"
133
+ className="px-4 py-2 bg-gradient-to-r from-indigo-500 to-indigo-700 text-white rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all"
134
+ >
135
+ Get Started
136
+ </Link>
137
+ </div>
138
+ ) : (
139
+ <div className="relative" ref={profileRef}>
140
+ <button
141
+ onClick={() => setIsProfileOpen(!isProfileOpen)}
142
+ className="w-10 h-10 rounded-full border-2 border-gray-300 dark:border-gray-600 overflow-hidden flex items-center justify-center bg-gray-200 dark:bg-gray-700"
143
+ >
144
+ {profilePic ? (
145
+ <img src={profilePic} alt="Profile" className="w-full h-full object-cover" />
146
+ ) : (
147
+ <UserIcon size={20} className="text-black dark:text-white" />
148
+ )}
149
+ </button>
150
+
151
+ {isProfileOpen && (
152
+ <div className="absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 rounded-2xl shadow-xl border border-gray-200 dark:border-gray-700 p-4 z-50">
153
+ <div className="flex items-center gap-3">
154
+ {profilePic ? (
155
+ <img src={profilePic} alt="Profile" className="w-12 h-12 rounded-full object-cover" />
156
+ ) : (
157
+ <UserIcon size={40} className="text-black dark:text-white" />
158
+ )}
159
+ <div>
160
+ <h4 className="text-gray-900 dark:text-gray-100 font-semibold">
161
+ {user.displayName || 'User'}
162
+ </h4>
163
+ <p className="text-sm text-gray-500 dark:text-gray-400">{user.email}</p>
164
+ </div>
165
+ </div>
166
+ <hr className="my-3 border-gray-200 dark:border-gray-700" />
167
+ <Link
168
+ to="/profile"
169
+ className="flex items-center gap-2 text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors py-2"
170
+ >
171
+ <UserIcon size={18} /> My Profile
172
+ </Link>
173
+ <button
174
+ onClick={() => setShowSignOutConfirm(true)} // show modal
175
+ className="flex items-center gap-2 text-gray-700 dark:text-gray-300 hover:text-red-600 dark:hover:text-red-400 transition-colors py-2 w-full text-left"
176
+ >
177
+ <LogOut size={18} /> Sign Out
178
+ </button>
179
+ </div>
180
+ )}
181
+ </div>
182
+ )}
183
+
184
+ {/* Mobile menu button */}
185
+ <button
186
+ onClick={() => setIsMenuOpen(!isMenuOpen)}
187
+ className="md:hidden p-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300"
188
+ aria-label={isMenuOpen ? "Close menu" : "Open menu"}
189
+ >
190
+ {isMenuOpen ? <X size={20} /> : <Menu size={20} />}
191
+ </button>
192
+ </div>
193
+ </div>
194
+
195
+ {/* Mobile Navigation */}
196
+ {isMenuOpen && (
197
+ <div className="md:hidden border-t border-gray-200 dark:border-gray-700 py-4">
198
+ <nav className="flex flex-col space-y-2">
199
+ {navLinks.map((link) => (
200
+ <Link
201
+ key={link.path}
202
+ to={link.path}
203
+ onClick={() => setIsMenuOpen(false)}
204
+ className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
205
+ isActive(link.path)
206
+ ? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20'
207
+ : 'text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400'
208
+ }`}
209
+ >
210
+ {link.label}
211
+ </Link>
212
+ ))}
213
+
214
+ {/* Signed-in user: Show Sign Out */}
215
+ {user ? (
216
+ <button
217
+ onClick={() => {
218
+ setIsMenuOpen(false);
219
+ setShowSignOutConfirm(true);
220
+ }}
221
+ className="px-3 py-2 rounded-md text-left text-gray-700 dark:text-gray-300 hover:text-red-600 dark:hover:text-red-400 transition-colors font-medium"
222
+ >
223
+ <LogOut size={16} className="inline mr-2" /> Sign Out
224
+ </button>
225
+ ) : (
226
+ <>
227
+ {/* Show Sign In if not signed in */}
228
+ <Link
229
+ to="/signin"
230
+ onClick={() => setIsMenuOpen(false)}
231
+ className="px-3 py-2 rounded-md text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
232
+ >
233
+ Sign In
234
+ </Link>
235
+
236
+ {/* Get Started Button */}
237
+ <Link
238
+ to="/get-started"
239
+ onClick={() => setIsMenuOpen(false)}
240
+ className="px-3 py-2 rounded-md text-sm font-semibold text-white bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 transition-colors text-center"
241
+ >
242
+ Get Started
243
+ </Link>
244
+ </>
245
+ )}
246
+ </nav>
247
+ </div>
248
+ )}
249
+ </div>
250
+
251
+ {/* Sign Out Confirmation Modal */}
252
+ {showSignOutConfirm && (
253
+ <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
254
+ <div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-xl w-80 text-center">
255
+ <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
256
+ Confirm Sign Out
257
+ </h2>
258
+ <p className="text-gray-700 dark:text-gray-300 mb-6">
259
+ Are you sure you want to sign out?
260
+ </p>
261
+ <div className="flex justify-center gap-4">
262
+ <button
263
+ onClick={handleSignOut}
264
+ className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition"
265
+ >
266
+ Yes, Sign Out
267
+ </button>
268
+ <button
269
+ onClick={() => setShowSignOutConfirm(false)}
270
+ className="px-4 py-2 bg-gray-300 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-600 transition"
271
+ >
272
+ Cancel
273
+ </button>
274
+ </div>
275
+ </div>
276
+ </div>
277
+ )}
278
+ </header>
279
+ );
280
+ };
281
+
282
+ export default Header;
src/components/HumanizerInterface.tsx ADDED
@@ -0,0 +1,915 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/components/HumanizerInterface.tsx
2
+ /**
3
+ * HumanizerInterface.tsx
4
+ * -------------------------------------------------------------------------------------
5
+ * A complete, production-ready Humanizer UI module with session analytics wired to
6
+ * Firestore and a hash-based cache layer. This file is intentionally verbose and
7
+ * heavily commented for clarity, with defensive checks for all Firestore updates.
8
+ *
9
+ * Key Features
10
+ * ------------
11
+ * - Modes: Normal, Advanced, Expert
12
+ * - Upload .txt / .docx
13
+ * - Hash-based cache lookup to avoid repeated processing (Firestore)
14
+ * - Session analytics:
15
+ * • stats.totalSessions (increment when user begins a session)
16
+ * • stats.averageSessionTime (weighted running average in minutes)
17
+ * - Content stats:
18
+ * • stats.textsHumanized (increment when output produced)
19
+ * • stats.wordsProcessed (increment by input word count)
20
+ * - Activity logging to `user_activities`
21
+ * - Clean UI with Tailwind classes, light/dark support via ThemeContext
22
+ *
23
+ * Dependencies
24
+ * ------------
25
+ * - React, React Router (`useNavigate`)
26
+ * - Firebase: `auth`, `db` provided from project-level firebase config
27
+ * - Firestore: doc, updateDoc, getDoc, getDocs, addDoc, collection,
28
+ * Timestamp, increment, where, query
29
+ * - Hash: sha256 (crypto-hash)
30
+ * - Mammoth: dynamic import for DOCX extraction
31
+ * - lucide-react: icons
32
+ *
33
+ * Firestore Structure (expected)
34
+ * ------------------------------
35
+ * users/{uid}:
36
+ * - email
37
+ * - stats: {
38
+ * textsHumanized: number,
39
+ * wordsProcessed: number,
40
+ * gptAverage: number, // not changed here, used elsewhere
41
+ * totalSessions: number,
42
+ * averageSessionTime: number // minutes, rounded to integer
43
+ * }
44
+ * - preferences: { theme, ... }
45
+ * - lastActive: ISO string
46
+ *
47
+ * user_activities (collection):
48
+ * - { email, action, mode, wordCount, timestamp, type }
49
+ *
50
+ * humanized_texts (collection):
51
+ * - { userId, originalText, textHash, humanizedText, createdAt }
52
+ *
53
+ * Notes
54
+ * -----
55
+ * - averageSessionTime is stored in MINUTES as an integer weighted running average.
56
+ * - totalSessions is incremented at "session start" (when user clicks "Humanize").
57
+ * - We compute duration on completion (cache hit or API success) and update running avg.
58
+ * - If session fails to produce output, we simply do not update the average (the session
59
+ * counter already went up; you can adapt this if you want to only count successful
60
+ * sessions by moving the increment to the success branch).
61
+ * - To keep the UX snappy and resilient, stats updates are "fire-and-forget" where safe,
62
+ * and wrapped with try/catch.
63
+ *
64
+ * -------------------------------------------------------------------------------------
65
+ */
66
+
67
+ import React, {
68
+ useState,
69
+ useContext,
70
+ useMemo,
71
+ useRef,
72
+ useCallback,
73
+ useEffect,
74
+ } from "react";
75
+ import { ThemeContext } from "../contexts/ThemeContext";
76
+ import { sha256 } from "crypto-hash";
77
+ import ReactMarkdown from "react-markdown";
78
+ import remarkGfm from "remark-gfm";
79
+ import {
80
+ Wand2,
81
+ Copy,
82
+ Download,
83
+ AlertCircle,
84
+ CheckCircle,
85
+ Upload,
86
+ Info,
87
+ } from "lucide-react";
88
+ import { useNavigate } from "react-router-dom";
89
+ import { auth, db } from "../firebase";
90
+ import {
91
+ doc,
92
+ updateDoc,
93
+ increment,
94
+ addDoc,
95
+ collection,
96
+ Timestamp,
97
+ query,
98
+ where,
99
+ getDocs,
100
+ getDoc,
101
+ Firestore,
102
+ DocumentReference,
103
+ DocumentData,
104
+ } from "firebase/firestore";
105
+
106
+ /* ---------------------------------------------------------------------------------- */
107
+ /* Types */
108
+ /* ---------------------------------------------------------------------------------- */
109
+
110
+ type HumanizeMode = "normal" | "advanced" | "expert";
111
+
112
+ interface HumanizerProps {
113
+ onModeChange?: (mode: HumanizeMode) => void;
114
+ }
115
+
116
+ /**
117
+ * Firestore user.stats model (subset used here).
118
+ */
119
+ interface UserStats {
120
+ textsHumanized?: number;
121
+ wordsProcessed?: number;
122
+ gptAverage?: number;
123
+ totalSessions?: number;
124
+ averageSessionTime?: number; // minutes
125
+ }
126
+
127
+ /**
128
+ * Firestore user record (subset used here).
129
+ */
130
+ interface UserDoc {
131
+ email?: string;
132
+ stats?: UserStats;
133
+ preferences?: {
134
+ theme?: "dark" | "light";
135
+ [k: string]: unknown;
136
+ };
137
+ [k: string]: unknown;
138
+ }
139
+
140
+ /**
141
+ * Response shape from the backend humanizer API (`/api/routes.php`).
142
+ */
143
+ interface HumanizerAPIResponse {
144
+ success: boolean;
145
+ humanizedText?: string;
146
+ error?: string;
147
+ }
148
+
149
+ /**
150
+ * Record in `humanized_texts` collection.
151
+ */
152
+ interface HumanizedTextRecord {
153
+ userId: string;
154
+ originalText: string; // stored for convenience (you can remove if sensitive)
155
+ textHash: string; // sha256 of originalText
156
+ humanizedText: string;
157
+ createdAt: Timestamp;
158
+ }
159
+
160
+ /* ---------------------------------------------------------------------------------- */
161
+ /* Utilities */
162
+ /* ---------------------------------------------------------------------------------- */
163
+
164
+ /**
165
+ * Safely returns the current user or null.
166
+ */
167
+ const getCurrentUser = () => auth.currentUser ?? null;
168
+
169
+ /**
170
+ * Convert a duration in milliseconds to a whole number of minutes, lower-bounded at 0.
171
+ * We round down to keep a stable integer for averageSessionTime.
172
+ */
173
+ const msToWholeMinutes = (ms: number): number => {
174
+ const mins = Math.floor(ms / 60000);
175
+ return mins < 0 ? 0 : mins;
176
+ };
177
+
178
+ /**
179
+ * Weighted running average:
180
+ * newAvg = (prevAvg * prevCount + newValue) / (prevCount + 1)
181
+ * Caller must supply the counts correctly (e.g., totalSessions already includes
182
+ * this new session, or not, depending on where you do the increment).
183
+ */
184
+ const weightedAverage = (
185
+ prevAvg: number,
186
+ prevCount: number,
187
+ newValue: number
188
+ ) => {
189
+ if (!Number.isFinite(prevAvg) || prevAvg < 0) prevAvg = 0;
190
+ if (!Number.isFinite(prevCount) || prevCount < 0) prevCount = 0;
191
+ if (!Number.isFinite(newValue) || newValue < 0) newValue = 0;
192
+
193
+ // If prevCount is 0, average is simply newValue
194
+ if (prevCount === 0) return newValue;
195
+
196
+ // Normal running average for (prevCount + 1) samples
197
+ return (prevAvg * prevCount + newValue) / (prevCount + 1);
198
+ };
199
+
200
+ /**
201
+ * Count words in a text by splitting on whitespace.
202
+ */
203
+ const countWords = (t: string) => (t.trim() ? t.trim().split(/\s+/).length : 0);
204
+
205
+ /**
206
+ * Defensive localStorage helper (optional use).
207
+ */
208
+ const safeLocalSet = (key: string, value: unknown) => {
209
+ try {
210
+ localStorage.setItem(key, JSON.stringify(value));
211
+ } catch {
212
+ /* no-op */
213
+ }
214
+ };
215
+
216
+ const safeLocalGet = <T,>(key: string, fallback: T): T => {
217
+ try {
218
+ const raw = localStorage.getItem(key);
219
+ return raw ? (JSON.parse(raw) as T) : fallback;
220
+ } catch {
221
+ return fallback;
222
+ }
223
+ };
224
+
225
+ /* ---------------------------------------------------------------------------------- */
226
+ /* Firestore Helper Functions */
227
+ /* ---------------------------------------------------------------------------------- */
228
+
229
+ /**
230
+ * Returns the user document reference for the current user, or null if not logged in.
231
+ */
232
+ const getUserRef = (dbInst: Firestore): DocumentReference<DocumentData> | null =>
233
+ getCurrentUser() ? doc(dbInst, "users", getCurrentUser()!.uid) : null;
234
+
235
+ /**
236
+ * Increment `stats.totalSessions` at session *start*. This function does not await the
237
+ * result (fire-and-forget); you can await if you want strict ordering.
238
+ */
239
+ const incrementTotalSessions = async (dbInst: Firestore) => {
240
+ const userRef = getUserRef(dbInst);
241
+ if (!userRef) return;
242
+ try {
243
+ await updateDoc(userRef, {
244
+ "stats.totalSessions": increment(1),
245
+ });
246
+ } catch (e) {
247
+ console.warn("[incrementTotalSessions] Failed to update total sessions:", e);
248
+ }
249
+ };
250
+
251
+ /**
252
+ * Update average session time with a new duration (in minutes).
253
+ * The logic:
254
+ * - We first read the current stats to obtain current average and totalSessions.
255
+ * - We compute a new weighted average using totalSessions as the count.
256
+ * - We then write the new average.
257
+ *
258
+ * Note: We assume `totalSessions` was incremented at session start.
259
+ */
260
+ const updateAverageSessionTime = async (
261
+ dbInst: Firestore,
262
+ durationMinutes: number
263
+ ) => {
264
+ const userRef = getUserRef(dbInst);
265
+ if (!userRef) return;
266
+
267
+ try {
268
+ const snap = await getDoc(userRef);
269
+ if (!snap.exists()) return;
270
+
271
+ const user = snap.data() as UserDoc;
272
+ const prevAvg = user?.stats?.averageSessionTime || 0;
273
+ const totalSessions = user?.stats?.totalSessions || 0;
274
+
275
+ // If for any reason totalSessions is 0, treat as new sample (avoid div by zero).
276
+ const countForAvg = Math.max(totalSessions, 0);
277
+
278
+ // Weighted average with the *new* value included.
279
+ // Because totalSessions was incremented at session start, that value already
280
+ // represents the new count. We should use (countForAvg - 1) as prevCount.
281
+ const prevCount = Math.max(countForAvg - 1, 0);
282
+ const nextAvg = weightedAverage(prevAvg, prevCount, durationMinutes);
283
+
284
+ await updateDoc(userRef, {
285
+ "stats.averageSessionTime": Math.max(0, Math.round(nextAvg)), // store rounded integer minutes
286
+ });
287
+ } catch (e) {
288
+ console.warn(
289
+ "[updateAverageSessionTime] Failed to compute/write average:",
290
+ e
291
+ );
292
+ }
293
+ };
294
+
295
+ /**
296
+ * Update basic counters after successful result (either from cache or API).
297
+ * - textsHumanized +1
298
+ * - wordsProcessed +{wordCount}
299
+ * - lastActive (ISO string)
300
+ */
301
+ const bumpContentStats = async (dbInst: Firestore, wordCount: number) => {
302
+ const userRef = getUserRef(dbInst);
303
+ if (!userRef) return;
304
+ try {
305
+ await updateDoc(userRef, {
306
+ "stats.textsHumanized": increment(1),
307
+ "stats.wordsProcessed": increment(Math.max(0, wordCount)),
308
+ lastActive: new Date().toISOString(),
309
+ });
310
+ } catch (e) {
311
+ console.warn("[bumpContentStats] Failed to update stats:", e);
312
+ }
313
+ };
314
+
315
+ /**
316
+ * Log user activity to `user_activities`.
317
+ */
318
+ const logActivity = async (dbInst: Firestore, payload: Record<string, any>) => {
319
+ try {
320
+ await addDoc(collection(dbInst, "user_activities"), {
321
+ ...payload,
322
+ timestamp: Timestamp.fromDate(new Date()),
323
+ });
324
+ } catch (e) {
325
+ console.warn("[logActivity] Failed to write activity log:", e);
326
+ }
327
+ };
328
+
329
+ /**
330
+ * Query `humanized_texts` by (userId + textHash). Returns the first found record or null.
331
+ */
332
+ const findCachedHumanizedText = async (
333
+ dbInst: Firestore,
334
+ userId: string,
335
+ textHash: string
336
+ ): Promise<HumanizedTextRecord | null> => {
337
+ try {
338
+ const q = query(
339
+ collection(dbInst, "humanized_texts"),
340
+ where("userId", "==", userId),
341
+ where("textHash", "==", textHash)
342
+ );
343
+ const snap = await getDocs(q);
344
+ if (snap.empty) return null;
345
+ return snap.docs[0].data() as HumanizedTextRecord;
346
+ } catch (e) {
347
+ console.warn("[findCachedHumanizedText] Query error:", e);
348
+ return null;
349
+ }
350
+ };
351
+
352
+ /**
353
+ * Persist a new record into `humanized_texts`.
354
+ */
355
+ const saveHumanizedText = async (
356
+ dbInst: Firestore,
357
+ record: Omit<HumanizedTextRecord, "createdAt">
358
+ ) => {
359
+ try {
360
+ await addDoc(collection(dbInst, "humanized_texts"), {
361
+ ...record,
362
+ createdAt: Timestamp.fromDate(new Date()),
363
+ });
364
+ } catch (e) {
365
+ console.warn("[saveHumanizedText] Failed to save cache record:", e);
366
+ }
367
+ };
368
+
369
+ /* ---------------------------------------------------------------------------------- */
370
+ /* Component: Humanizer */
371
+ /* ---------------------------------------------------------------------------------- */
372
+
373
+ const HumanizerInterface: React.FC<HumanizerProps> = ({ onModeChange }) => {
374
+ const { theme } = useContext(ThemeContext);
375
+ const navigate = useNavigate();
376
+
377
+ // ----------------------------------------------------------------------------------
378
+ // UI State
379
+ // ----------------------------------------------------------------------------------
380
+ const [inputText, setInputText] = useState<string>("");
381
+ const [outputText, setOutputText] = useState<string>("");
382
+ const [mode, setMode] = useState<HumanizeMode>("normal");
383
+ const [loading, setLoading] = useState<boolean>(false);
384
+ const [error, setError] = useState<string>("");
385
+ const [fileName, setFileName] = useState<string>("");
386
+ const [copied, setCopied] = useState<boolean>(false);
387
+
388
+ // Session start timestamp (ms). Reset on completion or error.
389
+ const sessionStartRef = useRef<number | null>(null);
390
+
391
+ // Memoized counts for UX
392
+ const inputWordCount = useMemo(() => countWords(inputText), [inputText]);
393
+ const outputWordCount = useMemo(() => countWords(outputText), [outputText]);
394
+
395
+ // Tailwind class shortcuts based on theme
396
+ const cardBg = theme === "dark" ? "bg-gray-800" : "bg-white";
397
+ const textPrimary = theme === "dark" ? "text-white" : "text-gray-900";
398
+ const textSecondary = theme === "dark" ? "text-gray-300" : "text-gray-600";
399
+ const borderCard = theme === "dark" ? "border-gray-700" : "border-gray-200";
400
+
401
+ // ----------------------------------------------------------------------------------
402
+ // Mode Handling
403
+ // ----------------------------------------------------------------------------------
404
+ const handleModeChange = (newMode: HumanizeMode) => {
405
+ setMode(newMode);
406
+ onModeChange?.(newMode);
407
+ };
408
+
409
+ // ----------------------------------------------------------------------------------
410
+ // File Upload (.txt / .docx)
411
+ // ----------------------------------------------------------------------------------
412
+ const handleFileUpload = async (
413
+ e: React.ChangeEvent<HTMLInputElement>
414
+ ): Promise<void> => {
415
+ const file = e.target.files?.[0];
416
+ if (!file) return;
417
+
418
+ setError("");
419
+ setFileName(file.name);
420
+
421
+ try {
422
+ if (file.type === "text/plain" || file.name.endsWith(".txt")) {
423
+ const text = await file.text();
424
+ setInputText(text);
425
+ } else if (
426
+ file.type ===
427
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ||
428
+ file.name.endsWith(".docx")
429
+ ) {
430
+ const reader = new FileReader();
431
+ reader.onload = async (event) => {
432
+ try {
433
+ const arrayBuffer = event.target?.result as ArrayBuffer;
434
+ // Dynamic import for lightweight initial bundle
435
+ const mammoth = await import("mammoth");
436
+ const result = await mammoth.extractRawText({ arrayBuffer });
437
+ setInputText(result.value || "");
438
+ } catch (docxErr) {
439
+ console.error(docxErr);
440
+ setError("Failed to parse DOCX file");
441
+ }
442
+ };
443
+ reader.readAsArrayBuffer(file);
444
+ } else {
445
+ setError("Unsupported file type. Please upload .txt or .docx");
446
+ }
447
+ } catch (err) {
448
+ console.error(err);
449
+ setError("Error reading file");
450
+ }
451
+ };
452
+
453
+ // ----------------------------------------------------------------------------------
454
+ // Hash-based Cache Lookup
455
+ // ----------------------------------------------------------------------------------
456
+ const checkHumanizedText = useCallback(
457
+ async (text: string): Promise<HumanizedTextRecord | null> => {
458
+ const user = getCurrentUser();
459
+ if (!user) return null;
460
+
461
+ try {
462
+ const textHash = await sha256(text);
463
+ const cached = await findCachedHumanizedText(db, user.uid, textHash);
464
+ return cached;
465
+ } catch (e) {
466
+ console.warn("[checkHumanizedText] Error:", e);
467
+ return null;
468
+ }
469
+ },
470
+ []
471
+ );
472
+
473
+ // ----------------------------------------------------------------------------------
474
+ // Copy & Download helpers
475
+ // ----------------------------------------------------------------------------------
476
+ const copyOutput = () => {
477
+ if (!outputText) return;
478
+ navigator.clipboard.writeText(outputText);
479
+ setCopied(true);
480
+ window.setTimeout(() => setCopied(false), 2000);
481
+ };
482
+
483
+ const downloadOutput = () => {
484
+ if (!outputText) return;
485
+ const blob = new Blob([outputText], { type: "text/plain" });
486
+ const url = URL.createObjectURL(blob);
487
+ const a = document.createElement("a");
488
+ a.href = url;
489
+ a.download = `humanized-text-${Date.now()}.txt`;
490
+ a.click();
491
+ URL.revokeObjectURL(url);
492
+ };
493
+
494
+ // ----------------------------------------------------------------------------------
495
+ // Session Flow
496
+ // ----------------------------------------------------------------------------------
497
+
498
+ /**
499
+ * Entry point from the "Humanize" button.
500
+ * 1) Ensures user is logged in (redirects if not).
501
+ * 2) Saves session start time.
502
+ * 3) Increments totalSessions immediately.
503
+ * 4) Calls humanizeText() which handles cache/API + result writes.
504
+ */
505
+ const handleHumanizeClick = async () => {
506
+ const user = getCurrentUser();
507
+ if (!user) {
508
+ navigate("/login");
509
+ return;
510
+ }
511
+
512
+ // Start a new session
513
+ sessionStartRef.current = Date.now();
514
+
515
+ // Count this as a session start (you can move this to success if you only
516
+ // want to count successful sessions).
517
+ await incrementTotalSessions(db);
518
+
519
+ // Proceed with processing
520
+ await humanizeText();
521
+ };
522
+
523
+ /**
524
+ * Compute duration since session start and write to average if we have a start time.
525
+ * We use "minutes" as our stored unit (rounded).
526
+ */
527
+ const finalizeSessionDuration = async () => {
528
+ if (!sessionStartRef.current) return;
529
+ const ms = Date.now() - sessionStartRef.current;
530
+ const durationMinutes = msToWholeMinutes(ms);
531
+ await updateAverageSessionTime(db, durationMinutes);
532
+ sessionStartRef.current = null;
533
+ };
534
+
535
+ // ----------------------------------------------------------------------------------
536
+ // Humanization Orchestration
537
+ // ----------------------------------------------------------------------------------
538
+
539
+ /**
540
+ * Central function that:
541
+ * - Validates input
542
+ * - Checks cache
543
+ * - Calls the API if needed
544
+ * - Writes Firestore stats and activity logs
545
+ * - Updates average session time
546
+ */
547
+ const humanizeText = async () => {
548
+ if (!inputText.trim()) {
549
+ setError("Please enter or upload text to humanize");
550
+ return;
551
+ }
552
+
553
+ const wordCount = countWords(inputText);
554
+ if (wordCount > 6000) {
555
+ setError(`Word limit exceeded. Max 6,000 words. Current: ${wordCount}.`);
556
+ return;
557
+ }
558
+
559
+ const user = getCurrentUser();
560
+ if (!user) {
561
+ setError("Please log in to continue");
562
+ navigate("/login");
563
+ return;
564
+ }
565
+
566
+ try {
567
+ setLoading(true);
568
+ setError("");
569
+
570
+ // Step 1: Cache lookup
571
+ const cached = await checkHumanizedText(inputText);
572
+ if (cached?.humanizedText) {
573
+ setOutputText(cached.humanizedText);
574
+
575
+ // Step 2a: Update stats for a successful "result" (from cache)
576
+ await bumpContentStats(db, wordCount);
577
+
578
+ // Step 2b: Update average session time
579
+ await finalizeSessionDuration();
580
+
581
+ // Step 2c: Log activity
582
+ await logActivity(db, {
583
+ email: user.email ?? null,
584
+ action: "Humanized Text (cache)",
585
+ mode,
586
+ wordCount,
587
+ type: "humanizer",
588
+ });
589
+
590
+ return; // Done
591
+ }
592
+
593
+ // Step 2: Call backend API
594
+ const resp = await fetch("https://humanizerx.pro/api/routes.php", {
595
+ method: "POST",
596
+ headers: { "Content-Type": "application/json" },
597
+ body: JSON.stringify({
598
+ text: inputText,
599
+ mode,
600
+ userId: user.uid,
601
+ }),
602
+ });
603
+
604
+ let data: HumanizerAPIResponse | null = null;
605
+ try {
606
+ data = (await resp.json()) as HumanizerAPIResponse;
607
+ } catch {
608
+ data = null;
609
+ }
610
+
611
+ if (!resp.ok || !data || !data.success || !data.humanizedText) {
612
+ const msg =
613
+ data?.error ||
614
+ `Failed to humanize text (HTTP ${resp.status}). Please try again.`;
615
+ setError(msg);
616
+
617
+ // NOTE: If you only want to count successful sessions, consider decrementing
618
+ // totalSessions here, or move the increment to after success. We keep the counter
619
+ // as-is to reflect attempted sessions. We also skip average time update on error.
620
+ return;
621
+ }
622
+
623
+ // Step 3: Display result
624
+ setOutputText(data.humanizedText);
625
+
626
+ // Step 4: Save to cache (best-effort)
627
+ try {
628
+ await saveHumanizedText(db, {
629
+ userId: user.uid,
630
+ originalText: inputText, // keep or remove based on your privacy needs
631
+ textHash: await sha256(inputText),
632
+ humanizedText: data.humanizedText,
633
+ });
634
+ } catch {
635
+ /* non-fatal */
636
+ }
637
+
638
+ // Step 5: Update stats
639
+ await bumpContentStats(db, wordCount);
640
+
641
+ // Step 6: Average session time
642
+ await finalizeSessionDuration();
643
+
644
+ // Step 7: Log activity
645
+ await logActivity(db, {
646
+ email: user.email ?? null,
647
+ action: "Humanized Text",
648
+ mode,
649
+ wordCount,
650
+ type: "humanizer",
651
+ });
652
+ } catch (err: any) {
653
+ console.error(err);
654
+ setError(err?.message || "Error processing text");
655
+ // On error, we do not finalize session time; you can choose to do it if desired.
656
+ } finally {
657
+ setLoading(false);
658
+ }
659
+ };
660
+
661
+ // ----------------------------------------------------------------------------------
662
+ // Optional: Persist last used mode locally (so it survives reloads)
663
+ // ----------------------------------------------------------------------------------
664
+ useEffect(() => {
665
+ const lastMode = safeLocalGet<HumanizeMode>("humanizer:lastMode", "normal");
666
+ if (lastMode !== mode) {
667
+ setMode(lastMode);
668
+ }
669
+ // eslint-disable-next-line react-hooks/exhaustive-deps
670
+ }, []);
671
+
672
+ useEffect(() => {
673
+ safeLocalSet("humanizer:lastMode", mode);
674
+ }, [mode]);
675
+
676
+ // ----------------------------------------------------------------------------------
677
+ // Render
678
+ // ----------------------------------------------------------------------------------
679
+
680
+ return (
681
+ <div className="space-y-6">
682
+ {/* MODE SELECTION */}
683
+ <div className={`${cardBg} rounded-xl shadow-lg p-6`}>
684
+ <div className="flex items-start justify-between mb-4">
685
+ <h2 className={`text-lg font-semibold ${textPrimary}`}>
686
+ Humanization Mode
687
+ </h2>
688
+ <div
689
+ className={`flex items-center gap-2 text-xs px-2 py-1 rounded-md ${
690
+ theme === "dark"
691
+ ? "bg-gray-700 text-gray-300"
692
+ : "bg-gray-100 text-gray-600"
693
+ }`}
694
+ title="Modes adjust how aggressively the text is rephrased."
695
+ >
696
+ <Info size={14} />
697
+ <span>Modes adjust rewrite strength</span>
698
+ </div>
699
+ </div>
700
+
701
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
702
+ {([
703
+ { id: "normal", name: "Normal", desc: "Balanced humanization" },
704
+ { id: "advanced", name: "Advanced", desc: "Stronger rephrasing" },
705
+ { id: "expert", name: "Expert", desc: "Maximum transformation" },
706
+ ] as { id: HumanizeMode; name: string; desc: string }[]).map(
707
+ (modeOption) => (
708
+ <button
709
+ key={modeOption.id}
710
+ onClick={() => handleModeChange(modeOption.id)}
711
+ className={`p-3 rounded-lg border-2 transition-all text-left ${
712
+ mode === modeOption.id
713
+ ? "border-blue-500 bg-blue-50 dark:bg-blue-900/20"
714
+ : theme === "dark"
715
+ ? "border-gray-600 hover:border-gray-500"
716
+ : "border-gray-200 hover:border-gray-300"
717
+ }`}
718
+ >
719
+ <div className={`font-medium ${textPrimary}`}>
720
+ {modeOption.name}
721
+ </div>
722
+ <div className={`text-sm ${textSecondary}`}>
723
+ {modeOption.desc}
724
+ </div>
725
+ </button>
726
+ )
727
+ )}
728
+ </div>
729
+ </div>
730
+
731
+ {/* MAIN GRID */}
732
+ <div className="grid lg:grid-cols-2 gap-6">
733
+ {/* INPUT SECTION */}
734
+ <div
735
+ className={`${cardBg} rounded-xl shadow-lg p-6 border ${borderCard} space-y-4`}
736
+ >
737
+ <div className="flex justify-between items-center mb-2">
738
+ <h3 className={`text-sm font-medium ${textPrimary}`}>Input Text</h3>
739
+ <label className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm rounded-lg cursor-pointer hover:bg-blue-700 transition-colors shadow-md">
740
+ <Upload size={18} />
741
+ <span>Upload</span>
742
+ <input
743
+ type="file"
744
+ accept=".txt,.docx"
745
+ onChange={handleFileUpload}
746
+ className="hidden"
747
+ />
748
+ </label>
749
+ </div>
750
+
751
+ <div
752
+ className={`relative h-96 border rounded-xl shadow-inner transition-all duration-200 ${
753
+ theme === "dark"
754
+ ? "bg-gradient-to-br from-gray-800 to-gray-900 border-gray-700 text-white"
755
+ : "bg-gradient-to-br from-gray-50 to-white border-gray-300 text-gray-900"
756
+ }`}
757
+ >
758
+ <textarea
759
+ value={inputText}
760
+ onChange={(e) => setInputText(e.target.value)}
761
+ placeholder="Paste text here or upload a file..."
762
+ className="w-full h-full p-4 resize-none bg-transparent focus:ring-2 focus:ring-blue-500 focus:border-transparent rounded-xl"
763
+ style={{
764
+ lineHeight: "1.6",
765
+ fontSize: "0.95rem",
766
+ fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system",
767
+ }}
768
+ />
769
+ {fileName && (
770
+ <p
771
+ className={`absolute bottom-2 left-4 text-xs ${textSecondary} truncate max-w-[70%]`}
772
+ >
773
+ Uploaded: {fileName}
774
+ </p>
775
+ )}
776
+ </div>
777
+
778
+ <div className="flex justify-between items-center">
779
+ <span
780
+ className={`text-sm ${
781
+ inputWordCount > 6000
782
+ ? theme === "dark"
783
+ ? "text-red-400"
784
+ : "text-red-600"
785
+ : textSecondary
786
+ }`}
787
+ >
788
+ {inputWordCount.toLocaleString()} / 6,000 words
789
+ </span>
790
+
791
+ <button
792
+ onClick={handleHumanizeClick}
793
+ disabled={loading || !inputText.trim() || inputWordCount > 6000}
794
+ className="px-6 py-3 bg-gradient-to-r from-indigo-900 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 disabled:opacity-50 transition-all duration-200 flex items-center gap-2"
795
+ >
796
+ <Wand2 size={16} />
797
+ {loading ? "Humanizing..." : "Humanize Text"}
798
+ </button>
799
+ </div>
800
+ </div>
801
+ {/* OUTPUT SECTION */}
802
+ <div
803
+ className={`${cardBg} rounded-xl shadow-lg p-6 border ${borderCard} space-y-4`}
804
+ >
805
+ <div className="flex items-center justify-between">
806
+ <h3 className={`text-lg font-semibold ${textPrimary}`}>
807
+ Humanized Text
808
+ </h3>
809
+
810
+ <div className="flex gap-2">
811
+ {outputText && (
812
+ <>
813
+ <button
814
+ onClick={copyOutput}
815
+ className={`flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-300 ${
816
+ copied
817
+ ? "bg-green-500 text-white"
818
+ : "bg-gray-600 text-white hover:bg-gray-700"
819
+ }`}
820
+ >
821
+ <Copy size={16} />
822
+ {copied ? "Copied!" : "Copy"}
823
+ </button>
824
+
825
+ <button
826
+ onClick={downloadOutput}
827
+ className="flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
828
+ >
829
+ <Download size={16} />
830
+ Download
831
+ </button>
832
+ </>
833
+ )}
834
+ </div>
835
+ </div>
836
+
837
+ {loading ? (
838
+ <div className="flex flex-col items-center justify-center h-96">
839
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-indigo-600 mb-3"></div>
840
+ <p className={textSecondary}>Processing your text...</p>
841
+ </div>
842
+ ) : outputText ? (
843
+ <>
844
+ <div
845
+ className={`w-full h-96 p-4 border rounded-xl overflow-y-auto shadow-inner text-sm leading-relaxed prose prose-sm max-w-none ${
846
+ theme === "dark"
847
+ ? "prose-invert bg-gradient-to-br from-gray-800 to-gray-900 border-gray-700 text-gray-100"
848
+ : "bg-gradient-to-br from-gray-50 to-white border-gray-300 text-gray-800"
849
+ }`}
850
+ style={{ whiteSpace: "pre-wrap" }} // ✅ preserve spaces & blank lines
851
+ >
852
+ <ReactMarkdown remarkPlugins={[remarkGfm]}>
853
+ {outputText}
854
+ </ReactMarkdown>
855
+ </div>
856
+
857
+ <div className="flex justify-between items-center">
858
+ <span className={`text-sm ${textSecondary}`}>
859
+ {outputWordCount.toLocaleString()} words
860
+ </span>
861
+ <div
862
+ className={`flex items-center gap-2 ${
863
+ theme === "dark" ? "text-green-400" : "text-green-600"
864
+ }`}
865
+ >
866
+ <CheckCircle size={16} />
867
+ <span className="text-sm font-medium">
868
+ Text humanized successfully
869
+ </span>
870
+ </div>
871
+ </div>
872
+ </>
873
+ ) : (
874
+ <div
875
+ className={`flex flex-col items-center justify-center h-96 ${textSecondary}`}
876
+ >
877
+ <Wand2 size={48} className="mb-3 opacity-50" />
878
+ <p>Humanized text will appear here...</p>
879
+ </div>
880
+ )}
881
+ </div>
882
+ </div>
883
+
884
+ {/* ERROR STATE */}
885
+ {error && (
886
+ <div
887
+ className={`rounded-lg p-4 border ${
888
+ theme === "dark"
889
+ ? "bg-red-900/20 border-red-800 text-red-300"
890
+ : "bg-red-50 border-red-200 text-red-700"
891
+ }`}
892
+ >
893
+ <div className="flex items-center gap-2 text-sm">
894
+ <AlertCircle size={16} />
895
+ <span>{error}</span>
896
+ </div>
897
+ </div>
898
+ )}
899
+
900
+ {/* FOOTNOTE / HINTS */}
901
+ <div
902
+ className={`text-xs ${
903
+ theme === "dark" ? "text-gray-400" : "text-gray-500"
904
+ }`}
905
+ >
906
+ </div>
907
+ </div>
908
+ );
909
+ };
910
+
911
+ export default HumanizerInterface;
912
+
913
+ /* ---------------------------------------------------------------------------------- */
914
+ /* EOF */
915
+ /* ---------------------------------------------------------------------------------- */
src/components/ProfileHeader.tsx ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, ChangeEvent, useContext } from 'react';
2
+ import { Camera, Edit3, Save, X, Calendar, Globe } from 'lucide-react';
3
+ import { UserProfile } from '../types/user';
4
+ import { ThemeContext } from '../contexts/ThemeContext';
5
+
6
+ interface ProfileHeaderProps {
7
+ user: UserProfile;
8
+ isEditing: boolean;
9
+ editForm: any;
10
+ setEditForm: (form: any) => void;
11
+ onEdit: () => void;
12
+ onSave: () => void;
13
+ onCancel: () => void;
14
+ onAvatarChange: (file: File) => void;
15
+ }
16
+
17
+ const ProfileHeader: React.FC<ProfileHeaderProps> = ({
18
+ user,
19
+ isEditing,
20
+ editForm,
21
+ setEditForm,
22
+ onEdit,
23
+ onSave,
24
+ onCancel,
25
+ onAvatarChange,
26
+ }) => {
27
+ const { theme } = useContext(ThemeContext); // ✅ ThemeContext
28
+ const [avatarPreview, setAvatarPreview] = useState(user.avatar);
29
+
30
+ const handleAvatarUpload = (e: ChangeEvent<HTMLInputElement>) => {
31
+ if (e.target.files?.[0]) {
32
+ const file = e.target.files[0];
33
+ setAvatarPreview(URL.createObjectURL(file));
34
+ onAvatarChange(file);
35
+ }
36
+ };
37
+
38
+ // Inputs still adapt to theme
39
+ const inputBg = theme === 'dark'
40
+ ? 'bg-gray-800 text-white placeholder-gray-400'
41
+ : 'bg-gray-100 text-gray-900 placeholder-gray-500';
42
+
43
+ return (
44
+ <div className="relative rounded-2xl overflow-hidden shadow-sm">
45
+ {/* Gradient Background */}
46
+ <div
47
+ className="absolute inset-0 z-0"
48
+ style={{ background: 'linear-gradient(to right, #1e3a8a, #0f172a)' }}
49
+ />
50
+
51
+ <div className="relative px-6 pb-6 pt-6 sm:pt-12 flex flex-col sm:flex-row items-start sm:items-end gap-6 z-10">
52
+ {/* Avatar */}
53
+ <div className="relative">
54
+ <img
55
+ src={avatarPreview || 'https://via.placeholder.com/150'}
56
+ alt={`${user.name || 'User'} Profile Picture`}
57
+ title={`${user.name || 'User'}'s profile avatar - Click camera icon to change`}
58
+ className="w-40 h-40 rounded-2xl object-cover border-0 shadow-lg"
59
+ />
60
+ <label className="absolute -bottom-2 -right-2 bg-blue-600 text-white p-2 rounded-xl hover:bg-blue-700 cursor-pointer transition shadow-lg">
61
+ <Camera className="w-4 h-4" />
62
+ <input type="file" className="hidden" onChange={handleAvatarUpload} />
63
+ </label>
64
+ </div>
65
+
66
+ {/* Name, Email, Username, Bio */}
67
+ <div className="flex-1 pt-16 sm:pt-4 text-left">
68
+ {isEditing ? (
69
+ <div className="space-y-4">
70
+ <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
71
+ <input
72
+ value={editForm.name}
73
+ onChange={(e) => setEditForm({ ...editForm, name: e.target.value })}
74
+ className={`px-4 py-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent ${inputBg}`}
75
+ placeholder="Full Name"
76
+ />
77
+ <input
78
+ value={editForm.nickname}
79
+ onChange={(e) => setEditForm({ ...editForm, nickname: e.target.value })}
80
+ className={`px-4 py-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent ${inputBg}`}
81
+ placeholder="Nickname"
82
+ />
83
+ </div>
84
+ <textarea
85
+ value={editForm.bio}
86
+ onChange={(e) => setEditForm({ ...editForm, bio: e.target.value })}
87
+ rows={3}
88
+ className={`w-full px-4 py-3 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent resize-none ${inputBg}`}
89
+ placeholder="Tell us about yourself..."
90
+ />
91
+ </div>
92
+ ) : (
93
+ <div className="text-white">
94
+ <h1 className="text-3xl font-bold mb-1">{user.name}</h1>
95
+ <p className="text-sm mb-1">{user.email}</p>
96
+ <p className="text-lg mb-2">@{user.username}</p>
97
+ <p className="mb-4 max-w-2xl">{user.bio}</p>
98
+
99
+ <div className="flex flex-wrap items-center gap-4 text-sm">
100
+ <div className="flex items-center gap-1">
101
+ <Calendar className="w-4 h-4 text-white" />
102
+ <span>Joined {user.joinDate}</span>
103
+ </div>
104
+ <div className="flex items-center gap-1">
105
+ <Globe className="w-4 h-4 text-white" />
106
+ <span>Last active: {user.lastActive}</span>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ )}
111
+ </div>
112
+
113
+ {/* Action Buttons */}
114
+ <div className="flex gap-3 pt-4 sm:pt-0 flex-col sm:flex-row">
115
+ {isEditing ? (
116
+ <>
117
+ <button
118
+ onClick={onSave}
119
+ className="flex items-center gap-2 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition font-medium"
120
+ >
121
+ <Save className="w-4 h-4" /> Save Changes
122
+ </button>
123
+ <button
124
+ onClick={onCancel}
125
+ className="flex items-center gap-2 px-6 py-2 rounded-lg hover:bg-gray-200 transition font-medium"
126
+ >
127
+ <X className="w-4 h-4" /> Cancel
128
+ </button>
129
+ </>
130
+ ) : (
131
+ <button
132
+ onClick={onEdit}
133
+ className="flex items-center gap-2 px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition font-medium"
134
+ >
135
+ <Edit3 className="w-4 h-4" /> Edit Profile
136
+ </button>
137
+ )}
138
+ </div>
139
+ </div>
140
+ </div>
141
+ );
142
+ };
143
+
144
+ export default ProfileHeader;
src/components/SettingsPanel.tsx ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useContext, useEffect } from 'react';
2
+ import {
3
+ Bell, Globe, Moon, Sun, Monitor, Lock,
4
+ Smartphone, Mail, LogOut, Trash2
5
+ } from 'lucide-react';
6
+ import { UserProfile, UserPreferences } from '../types/user';
7
+ import { ThemeContext } from '../contexts/ThemeContext';
8
+
9
+ interface SettingsPanelProps {
10
+ user: UserProfile | null;
11
+ onUpdatePreferences: (preferences: Partial<UserPreferences>) => void;
12
+ onSignOut: () => void;
13
+ onDeleteAccount: () => void;
14
+ }
15
+
16
+ const SettingsPanel: React.FC<SettingsPanelProps> = ({
17
+ user,
18
+ onUpdatePreferences,
19
+ onSignOut,
20
+ onDeleteAccount
21
+ }) => {
22
+ const [activeSection, setActiveSection] = useState('general');
23
+ const { theme, preference, setPreference } = useContext(ThemeContext);
24
+
25
+ const [localPreferences, setLocalPreferences] = useState<UserPreferences>({
26
+ timezone: 'UTC',
27
+ emailNotifications: false,
28
+ pushNotifications: false,
29
+ marketingEmails: false,
30
+ twoFactorEnabled: false,
31
+ profileVisibility: 'public',
32
+ theme: preference || 'dark',
33
+ ...(user?.preferences || {}),
34
+ });
35
+
36
+ const [showSignOutModal, setShowSignOutModal] = useState(false);
37
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
38
+
39
+ // Wait for user
40
+ if (!user) return <div className="p-6 text-gray-700">Loading...</div>;
41
+
42
+ // Sync system theme if system is selected
43
+ useEffect(() => {
44
+ if (localPreferences.theme !== 'system') return;
45
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
46
+ const listener = () => setPreference('system');
47
+ mediaQuery.addEventListener('change', listener);
48
+ return () => mediaQuery.removeEventListener('change', listener);
49
+ }, [localPreferences.theme, setPreference]);
50
+
51
+ const handleSelectTheme = (value: 'light' | 'dark' | 'system') => {
52
+ setLocalPreferences(prev => ({ ...prev, theme: value }));
53
+ setPreference(value);
54
+ onUpdatePreferences({ theme: value });
55
+ };
56
+
57
+ const handleToggle = (key: keyof UserPreferences) => {
58
+ if (typeof localPreferences[key] === 'boolean') {
59
+ const updated = { ...localPreferences, [key]: !localPreferences[key] };
60
+ setLocalPreferences(updated);
61
+ onUpdatePreferences({ [key]: updated[key] });
62
+ }
63
+ };
64
+
65
+ const sections = [
66
+ { id: 'general', label: 'General', icon: Globe },
67
+ { id: 'notifications', label: 'Notifications', icon: Bell },
68
+ { id: 'account', label: 'Account', icon: Lock }
69
+ ];
70
+
71
+ const bgClass = theme === 'dark' ? 'bg-gray-900' : 'bg-white';
72
+ const borderClass = theme === 'dark' ? 'border-gray-700' : 'border-gray-200';
73
+ const textClass = theme === 'dark' ? 'text-white' : 'text-gray-900';
74
+ const secondaryTextClass = theme === 'dark' ? 'text-gray-300' : 'text-gray-700';
75
+ const hoverBgClass = theme === 'dark' ? 'hover:bg-gray-800' : 'hover:bg-gray-100';
76
+
77
+ // ===== Sections =====
78
+ const renderGeneralSettings = () => (
79
+ <div className="space-y-6">
80
+ <div>
81
+ <h3 className={`text-lg font-semibold mb-4 ${textClass}`}>Appearance</h3>
82
+ <div className="grid grid-cols-3 gap-3">
83
+ {[
84
+ { value: 'light', label: 'Light', icon: Sun },
85
+ { value: 'dark', label: 'Dark', icon: Moon },
86
+ { value: 'system', label: 'System', icon: Monitor }
87
+ ].map(({ value, label, icon: Icon }) => (
88
+ <button
89
+ key={value}
90
+ onClick={() => handleSelectTheme(value as 'light' | 'dark' | 'system')}
91
+ className={`flex items-center gap-2 p-3 rounded-lg border transition ${
92
+ localPreferences.theme === value
93
+ ? 'border-blue-500 bg-blue-600 text-white'
94
+ : `${borderClass} ${hoverBgClass} ${textClass}`
95
+ }`}
96
+ >
97
+ <Icon className="w-4 h-4" />
98
+ <span className="text-sm font-medium">{label}</span>
99
+ </button>
100
+ ))}
101
+ </div>
102
+ </div>
103
+
104
+ <div>
105
+ <h3 className={`text-lg font-semibold mb-4 ${textClass}`}>Timezone</h3>
106
+ <select
107
+ value={localPreferences.timezone}
108
+ onChange={e => {
109
+ const updated = { ...localPreferences, timezone: e.target.value };
110
+ setLocalPreferences(updated);
111
+ onUpdatePreferences({ timezone: e.target.value });
112
+ }}
113
+ className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${borderClass} ${bgClass} ${textClass}`}
114
+ >
115
+ <option value="UTC">UTC</option>
116
+ <option value="America/New_York">Eastern Time</option>
117
+ <option value="America/Chicago">Central Time</option>
118
+ <option value="America/Denver">Mountain Time</option>
119
+ <option value="America/Los_Angeles">Pacific Time</option>
120
+ <option value="Europe/London">London</option>
121
+ <option value="Europe/Paris">Paris</option>
122
+ <option value="Asia/Tokyo">Tokyo</option>
123
+ </select>
124
+ </div>
125
+ </div>
126
+ );
127
+
128
+ const renderNotificationSettings = () => (
129
+ <div className="space-y-6">
130
+ <h3 className={`text-lg font-semibold mb-4 ${textClass}`}>Notifications</h3>
131
+ {[
132
+ { label: 'Email Notifications', key: 'emailNotifications', icon: Mail },
133
+ { label: 'Push Notifications', key: 'pushNotifications', icon: Smartphone },
134
+ { label: 'Marketing Emails', key: 'marketingEmails', icon: Bell }
135
+ ].map(({ label, key, icon: Icon }) => (
136
+ <div key={key} className={`flex items-center justify-between p-4 border rounded-lg ${borderClass}`}>
137
+ <div className="flex items-center gap-3">
138
+ <Icon className={`w-5 h-5 ${secondaryTextClass}`} />
139
+ <p className={`font-medium ${textClass}`}>{label}</p>
140
+ </div>
141
+ <button
142
+ onClick={() => handleToggle(key as keyof UserPreferences)}
143
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition ${
144
+ localPreferences[key as keyof UserPreferences] ? 'bg-blue-600' : 'bg-gray-500'
145
+ }`}
146
+ >
147
+ <span
148
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition ${
149
+ localPreferences[key as keyof UserPreferences] ? 'translate-x-6' : 'translate-x-1'
150
+ }`}
151
+ />
152
+ </button>
153
+ </div>
154
+ ))}
155
+ </div>
156
+ );
157
+
158
+ const renderAccountSettings = () => (
159
+ <div className="space-y-6">
160
+ <h3 className={`text-lg font-semibold mb-4 ${textClass}`}>Account Information</h3>
161
+ <div className={`rounded-lg p-4 space-y-3 ${bgClass} ${borderClass}`}>
162
+ <div className="flex justify-between">
163
+ <span className={`text-sm font-medium ${secondaryTextClass}`}>Email</span>
164
+ <span className="text-sm font-medium">{user.email}</span>
165
+ </div>
166
+ <div className="flex justify-between">
167
+ <span className={`text-sm font-medium ${secondaryTextClass}`}>Username</span>
168
+ <span className="text-sm font-medium">@{user.username}</span>
169
+ </div>
170
+ <div className="flex justify-between">
171
+ <span className={`text-sm font-medium ${secondaryTextClass}`}>Member Since</span>
172
+ <span className="text-sm font-medium">{user.joinDate}</span>
173
+ </div>
174
+ </div>
175
+
176
+ <h3 className={`text-lg font-semibold mb-4 mt-6 ${textClass}`}>Danger Zone</h3>
177
+ <div className={`rounded-lg p-4 space-y-4 ${borderClass}`}>
178
+ <button
179
+ onClick={() => setShowSignOutModal(true)}
180
+ className="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition"
181
+ >
182
+ <LogOut className="w-4 h-4" /> Sign Out
183
+ </button>
184
+
185
+ <button
186
+ onClick={() => setShowDeleteModal(true)}
187
+ className="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-lg bg-red-600 text-white hover:bg-red-700 transition"
188
+ >
189
+ <Trash2 className="w-4 h-4" /> Delete Account
190
+ </button>
191
+ </div>
192
+ </div>
193
+ );
194
+
195
+ const renderContent = () => {
196
+ switch (activeSection) {
197
+ case 'general': return renderGeneralSettings();
198
+ case 'notifications': return renderNotificationSettings();
199
+ case 'account': return renderAccountSettings();
200
+ default: return renderGeneralSettings();
201
+ }
202
+ };
203
+
204
+ return (
205
+ <div className={`rounded-xl border overflow-hidden ${bgClass} ${borderClass}`}>
206
+ <div className={`border-b ${borderClass}`}>
207
+ <nav className="flex space-x-8 px-6">
208
+ {sections.map(section => {
209
+ const Icon = section.icon;
210
+ return (
211
+ <button
212
+ key={section.id}
213
+ onClick={() => setActiveSection(section.id)}
214
+ className={`flex items-center gap-2 py-4 px-2 border-b-2 font-medium text-sm transition ${
215
+ activeSection === section.id
216
+ ? 'border-blue-500 text-blue-500'
217
+ : `border-transparent ${secondaryTextClass} hover:text-gray-300 dark:hover:text-gray-300`
218
+ }`}
219
+ >
220
+ <Icon className="w-4 h-4" />
221
+ {section.label}
222
+ </button>
223
+ );
224
+ })}
225
+ </nav>
226
+ </div>
227
+ <div className="p-6">{renderContent()}</div>
228
+
229
+ {/* Sign Out Modal */}
230
+ {showSignOutModal && (
231
+ <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
232
+ <div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-xl w-80 text-center">
233
+ <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
234
+ Confirm Sign Out
235
+ </h2>
236
+ <p className="text-gray-700 dark:text-gray-300 mb-6">
237
+ Are you sure you want to sign out?
238
+ </p>
239
+ <div className="flex justify-center gap-4">
240
+ <button
241
+ onClick={() => {
242
+ onSignOut();
243
+ setShowSignOutModal(false);
244
+ }}
245
+ className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition"
246
+ >
247
+ Yes, Sign Out
248
+ </button>
249
+ <button
250
+ onClick={() => setShowSignOutModal(false)}
251
+ className="px-4 py-2 bg-gray-300 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-600 transition"
252
+ >
253
+ Cancel
254
+ </button>
255
+ </div>
256
+ </div>
257
+ </div>
258
+ )}
259
+
260
+ {/* Delete Account Modal */}
261
+ {showDeleteModal && (
262
+ <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
263
+ <div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-xl w-80 text-center">
264
+ <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4">
265
+ Confirm Delete Account
266
+ </h2>
267
+ <p className="text-gray-700 dark:text-gray-300 mb-6">
268
+ Are you sure you want to delete your account? This action cannot be undone.
269
+ </p>
270
+ <div className="flex justify-center gap-4">
271
+ <button
272
+ onClick={() => {
273
+ onDeleteAccount();
274
+ setShowDeleteModal(false);
275
+ }}
276
+ className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition"
277
+ >
278
+ Yes, Delete
279
+ </button>
280
+ <button
281
+ onClick={() => setShowDeleteModal(false)}
282
+ className="px-4 py-2 bg-gray-300 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded-lg hover:bg-gray-400 dark:hover:bg-gray-600 transition"
283
+ >
284
+ Cancel
285
+ </button>
286
+ </div>
287
+ </div>
288
+ </div>
289
+ )}
290
+
291
+ </div>
292
+ );
293
+ };
294
+
295
+ export default SettingsPanel;
src/components/StatsGrid.tsx ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/components/StatsGrid.tsx
2
+ import React, { useContext } from 'react';
3
+ import { TrendingUp, FileText, Zap, Target } from 'lucide-react';
4
+ import { ThemeContext } from '../contexts/ThemeContext';
5
+ import { useUserStats } from '../hooks/useUserStats';
6
+
7
+ const StatsGrid: React.FC = () => {
8
+ const { theme } = useContext(ThemeContext);
9
+ const { stats } = useUserStats(); // Live stats from Firestore
10
+ const isDark = theme === 'dark';
11
+
12
+ // --- Safely parse all stats ---
13
+ const textsHumanized = Number(stats?.textsHumanized ?? 0);
14
+ const wordsProcessed = Number(stats?.wordsProcessed ?? 0);
15
+ const totalSessions = Number(stats?.totalSessions ?? 0);
16
+ const gptAverage = Number(stats?.gptAverage ?? 0);
17
+
18
+ // --- Format derived stats ---
19
+ const gptAverageDisplay = `${gptAverage.toFixed(1)}%`;
20
+
21
+ const statItems = [
22
+ {
23
+ label: 'Texts Humanized',
24
+ value: textsHumanized.toLocaleString(),
25
+ icon: FileText,
26
+ color: 'blue',
27
+ change: '+12%',
28
+ },
29
+ {
30
+ label: 'Words Processed',
31
+ value: wordsProcessed.toLocaleString(),
32
+ icon: Zap,
33
+ color: 'green',
34
+ change: '+8%',
35
+ },
36
+ {
37
+ label: 'GPT Average',
38
+ value: gptAverageDisplay,
39
+ icon: Target,
40
+ color: 'purple',
41
+ change: '+3%',
42
+ },
43
+ {
44
+ label: 'Total Sessions',
45
+ value: totalSessions.toLocaleString(),
46
+ icon: TrendingUp,
47
+ color: 'orange',
48
+ change: '+15%',
49
+ },
50
+ ];
51
+
52
+ const getColorClasses = (color: string) => {
53
+ const colors = {
54
+ blue: `bg-${isDark ? 'blue-800' : 'blue-50'} text-${
55
+ isDark ? 'blue-300' : 'blue-600'
56
+ } border-${isDark ? 'blue-700' : 'blue-200'}`,
57
+ green: `bg-${isDark ? 'green-800' : 'green-50'} text-${
58
+ isDark ? 'green-300' : 'green-600'
59
+ } border-${isDark ? 'green-700' : 'green-200'}`,
60
+ purple: `bg-${isDark ? 'purple-800' : 'purple-50'} text-${
61
+ isDark ? 'purple-300' : 'purple-600'
62
+ } border-${isDark ? 'purple-700' : 'purple-200'}`,
63
+ orange: `bg-${isDark ? 'orange-800' : 'orange-50'} text-${
64
+ isDark ? 'orange-300' : 'orange-600'
65
+ } border-${isDark ? 'orange-700' : 'orange-200'}`,
66
+ };
67
+ return colors[color as keyof typeof colors] || colors.blue;
68
+ };
69
+
70
+ const cardBgClass = isDark
71
+ ? 'bg-gray-900 border-gray-700 text-white'
72
+ : 'bg-white border-gray-200 text-gray-900';
73
+
74
+ return (
75
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
76
+ {statItems.map((item, index) => {
77
+ const Icon = item.icon;
78
+ return (
79
+ <div
80
+ key={index}
81
+ className={`rounded-xl border p-6 hover:shadow-md transition-shadow ${cardBgClass}`}
82
+ >
83
+ <div className="flex items-center justify-between mb-4">
84
+ <div className={`p-3 rounded-lg ${getColorClasses(item.color)}`}>
85
+ <Icon className="w-6 h-6" />
86
+ </div>
87
+ <span
88
+ className={`text-sm font-medium px-2 py-1 rounded ${
89
+ isDark
90
+ ? 'bg-gray-800 text-green-400'
91
+ : 'bg-green-50 text-green-600'
92
+ }`}
93
+ >
94
+ {item.change}
95
+ </span>
96
+ </div>
97
+ <div>
98
+ <p
99
+ className={`text-2xl font-bold mb-1 ${
100
+ isDark ? 'text-white' : 'text-gray-900'
101
+ }`}
102
+ >
103
+ {item.value || '0'}
104
+ </p>
105
+ <p
106
+ className={`text-sm ${
107
+ isDark ? 'text-gray-300' : 'text-gray-600'
108
+ }`}
109
+ >
110
+ {item.label}
111
+ </p>
112
+ </div>
113
+ </div>
114
+ );
115
+ })}
116
+ </div>
117
+ );
118
+ };
119
+
120
+ export default StatsGrid;
src/contexts/ThemeContext.tsx ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/contexts/ThemeContext.tsx
2
+ import React, { createContext, useState, useEffect, ReactNode } from 'react';
3
+
4
+ type Theme = 'light' | 'dark';
5
+ type ThemePreference = Theme | 'dark';
6
+
7
+ interface ThemeContextType {
8
+ theme: Theme; // resolved theme
9
+ preference: ThemePreference; // user choice
10
+ setPreference: (pref: ThemePreference) => void;
11
+ toggleTheme: () => void;
12
+ }
13
+
14
+ export const ThemeContext = createContext<ThemeContextType>({
15
+ theme: 'dark',
16
+ preference: 'dark',
17
+ setPreference: () => {},
18
+ toggleTheme: () => {},
19
+ });
20
+
21
+ const getSystemTheme = (): Theme =>
22
+ window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
23
+
24
+ const getInitialPreference = (): ThemePreference => {
25
+ const saved = localStorage.getItem('theme-preference') as ThemePreference | null;
26
+ return saved || 'dark'; // default to dark
27
+ };
28
+
29
+ export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
30
+ const [preference, setPreferenceState] = useState<ThemePreference>(getInitialPreference);
31
+ const [theme, setTheme] = useState<Theme>(() =>
32
+ preference === 'system' ? getSystemTheme() : (preference as Theme)
33
+ );
34
+
35
+ // Watch system theme changes
36
+ useEffect(() => {
37
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
38
+ const handleChange = () => {
39
+ if (preference === 'system') {
40
+ setTheme(getSystemTheme());
41
+ }
42
+ };
43
+ mediaQuery.addEventListener('change', handleChange);
44
+ return () => mediaQuery.removeEventListener('change', handleChange);
45
+ }, [preference]);
46
+
47
+ // Update theme whenever preference changes
48
+ useEffect(() => {
49
+ const resolvedTheme = preference === 'system' ? getSystemTheme() : (preference as Theme);
50
+ setTheme(resolvedTheme);
51
+ document.documentElement.classList.toggle('dark', resolvedTheme === 'dark');
52
+ }, [preference]);
53
+
54
+ const setPreference = (pref: ThemePreference) => {
55
+ setPreferenceState(pref);
56
+ localStorage.setItem('theme-preference', pref);
57
+ };
58
+
59
+ const toggleTheme = () => {
60
+ if (preference === 'system') {
61
+ setPreference(theme === 'dark' ? 'light' : 'dark');
62
+ } else {
63
+ setPreference(preference === 'dark' ? 'light' : 'dark');
64
+ }
65
+ };
66
+
67
+ return (
68
+ <ThemeContext.Provider value={{ theme, preference, setPreference, toggleTheme }}>
69
+ {children}
70
+ </ThemeContext.Provider>
71
+ );
72
+ };
src/firebase.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/firebase.js
2
+
3
+ import { initializeApp } from "firebase/app";
4
+ import { getAuth, GoogleAuthProvider } from "firebase/auth";
5
+ import { getFirestore } from "firebase/firestore";
6
+ import { getStorage } from "firebase/storage";
7
+ import { getAnalytics, isSupported } from "firebase/analytics";
8
+
9
+ // 🔹 Your Firebase configuration
10
+ const firebaseConfig = {
11
+ apiKey: "AIzaSyDonQCMhJ5jQ3kiheFvAeWxJ8MtH1cRcXk",
12
+ authDomain: "humanizer-x-16baa.firebaseapp.com",
13
+ projectId: "humanizer-x-16baa",
14
+ storageBucket: "humanizer-x-16baa.appspot.com",
15
+ messagingSenderId: "513434319424",
16
+ appId: "1:513434319424:web:74912c65b196c62dc87da5",
17
+ measurementId: "G-H94EMGYRNF",
18
+ };
19
+
20
+ // ✅ Initialize Firebase app
21
+ const app = initializeApp(firebaseConfig);
22
+
23
+ // ✅ Firebase services exports
24
+ export const auth = getAuth(app);
25
+ export const provider = new GoogleAuthProvider();
26
+ export const db = getFirestore(app); // Firestore database
27
+ export const storage = getStorage(app); // Storage for profile images
28
+
29
+ // ✅ Analytics (optional)
30
+ export let analytics;
31
+ if (typeof window !== "undefined") {
32
+ isSupported().then((supported) => {
33
+ if (supported) {
34
+ analytics = getAnalytics(app);
35
+ }
36
+ });
37
+ }
38
+
39
+ // ✅ Optional init function (if needed elsewhere)
40
+ export async function initFirebase() {
41
+ return { app, auth, db, storage, provider, analytics };
42
+ }
src/hooks/useUserStats.ts ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/hooks/useUserStats.ts
2
+ import { useEffect, useState } from 'react';
3
+ import { doc, onSnapshot, updateDoc, increment } from 'firebase/firestore';
4
+ import { db, auth } from '../firebase';
5
+ import { UserStats } from '../types/user';
6
+
7
+ export const useUserStats = () => {
8
+ const [stats, setStats] = useState<UserStats | undefined>(undefined);
9
+
10
+ useEffect(() => {
11
+ if (!auth.currentUser) return;
12
+
13
+ const userRef = doc(db, 'users', auth.currentUser.uid);
14
+ const unsubscribe = onSnapshot(userRef, async (docSnap) => {
15
+ if (docSnap.exists()) {
16
+ const data = docSnap.data();
17
+
18
+ // Ensure stats exist
19
+ let userStats: UserStats = data.stats || {
20
+ textsHumanized: 0,
21
+ wordsProcessed: 0,
22
+ gptAverage: 0,
23
+ totalSessions: 0,
24
+ totalDuration: 0,
25
+ averageSessionTime: 0,
26
+ };
27
+
28
+ // Backfill totalDuration if missing
29
+ if (userStats.totalDuration === undefined) {
30
+ const estimatedDuration =
31
+ (userStats.averageSessionTime ?? 0) * (userStats.totalSessions ?? 1);
32
+ await updateDoc(userRef, { 'stats.totalDuration': estimatedDuration });
33
+ userStats.totalDuration = estimatedDuration;
34
+ }
35
+
36
+ // Always recalc averageSessionTime
37
+ const avgTime =
38
+ (userStats.totalSessions && userStats.totalDuration)
39
+ ? userStats.totalDuration / userStats.totalSessions
40
+ : 0;
41
+
42
+ userStats.averageSessionTime = avgTime;
43
+
44
+ setStats(userStats);
45
+ }
46
+ });
47
+
48
+ return () => unsubscribe();
49
+ }, []);
50
+
51
+ const updateStats = async (newStats: Partial<UserStats>) => {
52
+ if (!auth.currentUser || !stats) return;
53
+
54
+ const userRef = doc(db, 'users', auth.currentUser.uid);
55
+
56
+ // Calculate new totalDuration if averageSessionTime or totalSessions are updated
57
+ let updatedTotalDuration = stats.totalDuration;
58
+ let updatedTotalSessions = stats.totalSessions;
59
+
60
+ if (newStats.totalSessions !== undefined) {
61
+ updatedTotalSessions += newStats.totalSessions;
62
+ }
63
+
64
+ if (newStats.averageSessionTime !== undefined) {
65
+ updatedTotalDuration = updatedTotalSessions * newStats.averageSessionTime;
66
+ } else if (newStats.totalDuration !== undefined) {
67
+ updatedTotalDuration = stats.totalDuration + newStats.totalDuration;
68
+ }
69
+
70
+ const newAvgSessionTime =
71
+ updatedTotalSessions > 0 ? updatedTotalDuration / updatedTotalSessions : 0;
72
+
73
+ await updateDoc(userRef, {
74
+ 'stats.textsHumanized':
75
+ newStats.textsHumanized !== undefined
76
+ ? increment(newStats.textsHumanized)
77
+ : increment(0),
78
+ 'stats.wordsProcessed':
79
+ newStats.wordsProcessed !== undefined
80
+ ? increment(newStats.wordsProcessed)
81
+ : increment(0),
82
+ 'stats.totalSessions':
83
+ newStats.totalSessions !== undefined
84
+ ? increment(newStats.totalSessions)
85
+ : increment(0),
86
+ 'stats.gptAverage':
87
+ newStats.gptAverage !== undefined ? newStats.gptAverage : stats.gptAverage,
88
+ 'stats.totalDuration': updatedTotalDuration,
89
+ 'stats.averageSessionTime': newAvgSessionTime,
90
+ });
91
+ };
92
+
93
+ return { stats, updateStats };
94
+ };
src/index.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
src/main.tsx ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { HelmetProvider } from "react-helmet-async";
4
+ import App from "./App.tsx";
5
+ import "./index.css";
6
+
7
+ // --- Apply saved theme immediately (default = dark) ---
8
+ const savedPref = localStorage.getItem("theme-preference") || "dark";
9
+
10
+ const getSystemTheme = () =>
11
+ window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
12
+
13
+ const resolved = savedPref === "system" ? getSystemTheme() : savedPref;
14
+ document.documentElement.classList.toggle("dark", resolved === "dark");
15
+
16
+ // --- Unregister any service workers to avoid WebContainer errors ---
17
+ if ("serviceWorker" in navigator) {
18
+ navigator.serviceWorker
19
+ .getRegistrations()
20
+ .then((registrations) => {
21
+ registrations.forEach((reg) => reg.unregister());
22
+ })
23
+ .catch((err) => console.warn("SW unregister failed:", err));
24
+ }
25
+
26
+ // --- Render React app ---
27
+ const container = document.getElementById("root");
28
+ if (container) {
29
+ createRoot(container).render(
30
+ <StrictMode>
31
+ <HelmetProvider>
32
+ <App />
33
+ </HelmetProvider>
34
+ </StrictMode>
35
+ );
36
+ } else {
37
+ console.error("Root element not found!");
38
+ }
src/pages/AboutPage.tsx ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useContext } from 'react';
2
+ import { Helmet } from 'react-helmet-async';
3
+ import { Users, Target, Award, Sparkles } from 'lucide-react';
4
+ import Header from '../components/Header';
5
+ import Footer from '../components/Footer';
6
+ import { ThemeContext } from '../contexts/ThemeContext';
7
+
8
+ const AboutPage: React.FC = () => {
9
+ const { theme } = useContext(ThemeContext);
10
+
11
+ // Helpers for conditional colors
12
+ const bgPage = theme === 'dark' ? 'bg-gray-900' : 'bg-gray-50';
13
+ const bgSection = theme === 'dark' ? 'bg-gray-800' : 'bg-white';
14
+ const textPrimary = theme === 'dark' ? 'text-white' : 'text-gray-900';
15
+ const textSecondary = theme === 'dark' ? 'text-gray-300' : 'text-gray-600';
16
+
17
+ return (
18
+ <div className={`min-h-screen transition-colors duration-300 ${bgPage}`}>
19
+ <Helmet>
20
+ <title>About HumanizerX – AI Text Humanizer & GPT Detector</title>
21
+ <meta
22
+ name="description"
23
+ content="Learn more about HumanizerX, our mission, and how we transform AI-generated content into natural, human-like text with authenticity and creativity."
24
+ />
25
+ <link rel="canonical" href="https://www.humanizerx.pro/about" />
26
+ </Helmet>
27
+
28
+ <Header currentPage="About" />
29
+ {/* Hero Section */}
30
+ <section className="bg-gradient-to-r from-indigo-900 to-slate-900 text-white py-20">
31
+ <div className="container mx-auto px-4 text-center">
32
+ <img
33
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
34
+ alt="Humanizer X - AI Text Humanization Tool Logo"
35
+ title="Humanizer X - About Us - Learn more about our AI humanization technology"
36
+ className="mx-auto w-40 h-30 object-contain mb-6"
37
+ />
38
+ <h1 className="text-5xl md:text-6xl font-bold leading-tight mb-6">
39
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
40
+ About
41
+ </span>{' '}
42
+ <span className="text-white">Us</span>
43
+ </h1>
44
+ <p className="text-lg md:text-xl max-w-3xl mx-auto text-blue-100">
45
+ Empowering creators worldwide by transforming AI-generated text into authentic, human-like content with precision and creativity.
46
+ </p>
47
+ </div>
48
+ </section>
49
+
50
+ <main className="container mx-auto px-4 py-12 max-w-6xl">
51
+ {/* Mission Section */}
52
+ <section className={`${bgSection} rounded-xl shadow-lg p-8 mb-12`}>
53
+ <div className="grid md:grid-cols-2 gap-8 items-start">
54
+ <div>
55
+ <h2 className={`text-2xl font-bold mb-4 ${textPrimary}`}>
56
+ Our Mission
57
+ </h2>
58
+ <p className={textSecondary}>
59
+ At{' '}
60
+ <a
61
+ href="https://humanizerx.pro"
62
+ target="_blank"
63
+ rel="noopener noreferrer"
64
+ className="text-indigo-600 font-bold hover:underline"
65
+ >
66
+ Humanizer <span className="text-indigo-500">X</span>
67
+ </a>
68
+ , we believe artificial intelligence should feel natural,
69
+ authentic, and truly human. Our mission is to bridge the gap
70
+ between AI precision and human creativity.
71
+ </p>
72
+
73
+ <p className={`mt-2 ${textSecondary}`}>
74
+ Humanizer <span className="text-indigo-500">X</span> empowers
75
+ creators, businesses, and professionals by transforming robotic
76
+ or generic AI text into content that feels real, engaging, and
77
+ uniquely human.
78
+ </p>
79
+
80
+ <p className={`mt-2 ${textSecondary}`}>
81
+ Our tool doesn’t just write words – it understands tone,
82
+ emotion, and intention. Whether you’re crafting marketing copy,
83
+ academic research, stories, or professional emails, Humanizer{' '}
84
+ <span className="text-indigo-500">X</span> makes your message
85
+ resonate with real people.
86
+ </p>
87
+
88
+ <h2 className={`text-2xl font-bold mt-6 ${textSecondary}`}>
89
+ Why Humanizer X?
90
+ </h2>
91
+ <ul className={`list-disc pl-6 space-y-2 mt-2 ${textSecondary}`}>
92
+ <li>
93
+ <span className="font-bold text-indigo-600">Natural Flow</span>{' '}
94
+ – written as if by a human, not a machine.
95
+ </li>
96
+ <li>
97
+ <span className="font-bold text-indigo-600">
98
+ Customizable Tone
99
+ </span>{' '}
100
+ – adapt to your brand, audience, or style.
101
+ </li>
102
+ <li>
103
+ <span className="font-bold text-indigo-600">
104
+ Seamless Integration
105
+ </span>{' '}
106
+ – ideal for writers, students, and professionals.
107
+ </li>
108
+ <li>
109
+ <span className="font-bold text-indigo-600">Ethical AI</span>{' '}
110
+ – prioritizing authenticity and transparency.
111
+ </li>
112
+ </ul>
113
+
114
+ <p className={`mt-2 ${textSecondary}`}>
115
+
116
+ We believe technology should amplify human expression – not
117
+ replace it. With{' '}
118
+ <a
119
+ href="https://humanizerx.pro"
120
+ target="_blank"
121
+ rel="noopener noreferrer"
122
+ className="text-indigo-600 font-semibold hover:underline"
123
+ >
124
+ Humanizer <span className="text-indigo-500">X</span>
125
+ </a>
126
+ , you get the best of both worlds: the efficiency of AI and the
127
+ creativity of humans.
128
+ </p>
129
+ </div>
130
+
131
+ {/* Features Boxes */}
132
+ <div className="flex flex-col justify-between h-full gap-6">
133
+ <div className="bg-gradient-to-br from-indigo-50 to-slate-50 dark:from-blue-900/20 dark:to-indigo-900/20 p-6 rounded-lg flex flex-col items-center text-center">
134
+ <div className="w-16 h-16 bg-gradient-to-br from-indigo-500 to-indigo-700 rounded-lg flex items-center justify-center mb-4">
135
+ <Target className="text-white" size={32} />
136
+ </div>
137
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>
138
+ Precision & Quality
139
+ </h3>
140
+ <p className={`text-sm ${textSecondary}`}>
141
+ Every word matters. Our algorithms ensure maximum accuracy while preserving the essence of your content.
142
+ </p>
143
+ </div>
144
+
145
+ <div className="bg-gradient-to-br from-indigo-50 to-slate-50 dark:from-blue-900/20 dark:to-indigo-900/20 p-6 rounded-lg flex flex-col items-center text-center">
146
+ <div className="w-16 h-16 bg-gradient-to-br from-indigo-500 to-indigo-700 rounded-lg flex items-center justify-center mb-4">
147
+ <Sparkles className="text-white" size={32} />
148
+ </div>
149
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>
150
+ Humanized Content
151
+ </h3>
152
+ <p className={`text-sm ${textSecondary}`}>
153
+ Transform generic AI text into engaging, authentic content that resonates naturally with readers.
154
+ </p>
155
+ </div>
156
+
157
+ <div className="bg-gradient-to-br from-indigo-50 to-slate-50 dark:from-blue-900/20 dark:to-indigo-900/20 p-6 rounded-lg flex flex-col items-center text-center">
158
+ <div className="w-16 h-16 bg-gradient-to-br from-indigo-500 to-indigo-700 rounded-lg flex items-center justify-center mb-4">
159
+ <Users className="text-white" size={32} />
160
+ </div>
161
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>
162
+ Customizable Experience
163
+ </h3>
164
+ <p className={`text-sm ${textSecondary}`}>
165
+ Tailor AI output to your preferred tone, style, and audience for a consistent and professional message.
166
+ </p>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </section>
171
+
172
+ {/* Features Grid */}
173
+ <section className="grid md:grid-cols-3 gap-6 mb-12">
174
+ <div className={`${bgSection} p-6 rounded-xl shadow-lg`}>
175
+ <div className="w-12 h-12 bg-blue-100 dark:bg-blue-900/30 rounded-lg flex items-center justify-center mb-4">
176
+ <Sparkles
177
+ className={`${theme === 'dark' ? 'text-blue-400' : 'text-blue-600'}`}
178
+ size={24}
179
+ />
180
+ </div>
181
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>Advanced AI Detection</h3>
182
+ <p className={`text-sm ${textSecondary}`}>
183
+ State-of-the-art algorithms that can identify AI-generated content with exceptional accuracy.
184
+ </p>
185
+ </div>
186
+
187
+ <div className={`${bgSection} p-6 rounded-xl shadow-lg`}>
188
+ <div className="w-12 h-12 bg-indigo-100 dark:bg-indigo-900/30 rounded-lg flex items-center justify-center mb-4">
189
+ <Users
190
+ className={`${theme === 'dark' ? 'text-indigo-400' : 'text-indigo-600'}`}
191
+ size={24}
192
+ />
193
+ </div>
194
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>Human-Centric Design</h3>
195
+ <p className={`text-sm ${textSecondary}`}>
196
+ Built with user experience in mind, featuring intuitive interfaces and powerful tools.
197
+ </p>
198
+ </div>
199
+
200
+ <div className={`${bgSection} p-6 rounded-xl shadow-lg`}>
201
+ <div className="w-12 h-12 bg-green-100 dark:bg-green-900/30 rounded-lg flex items-center justify-center mb-4">
202
+ <Award
203
+ className={`${theme === 'dark' ? 'text-green-400' : 'text-green-600'}`}
204
+ size={24}
205
+ />
206
+ </div>
207
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>Industry Leading</h3>
208
+ <p className={`text-sm ${textSecondary}`}>
209
+ Trusted by thousands of content creators, students, and professionals for reliable text humanization.
210
+ </p>
211
+ </div>
212
+ </section>
213
+ </main>
214
+
215
+ <Footer />
216
+ </div>
217
+ );
218
+ };
219
+
220
+ export default AboutPage;
src/pages/BlogPage.tsx ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useContext } from "react";
2
+ import { Helmet } from "react-helmet-async";
3
+
4
+ import Header from "../components/Header";
5
+ import Footer from "../components/Footer";
6
+ import { ThemeContext } from "../contexts/ThemeContext";
7
+
8
+ const BlogPage: React.FC = () => {
9
+ const { theme } = useContext(ThemeContext);
10
+
11
+ return (
12
+ <div
13
+ className={`min-h-screen flex flex-col transition-colors duration-300 ${
14
+ theme === "dark" ? "bg-gray-900 text-white" : "bg-gray-50 text-black"
15
+ }`}
16
+ >
17
+ <Helmet>
18
+ <title>HumanizerX Blog – Insights on AI Text Humanization</title>
19
+ <meta
20
+ name="description"
21
+ content="Explore HumanizerX Blog for the latest insights, updates, and tips on AI text humanization and technology."
22
+ />
23
+ <link rel="canonical" href="https://www.humanizerx.pro/blogs" />
24
+ </Helmet>
25
+
26
+ {/* ✅ Header at top */}
27
+ <Header currentPage="Blog" />
28
+
29
+ {/* ✅ iframe grows and page scrolls with header/footer */}
30
+ <main className="flex-1">
31
+ <iframe
32
+ src="https://www.humanizerx.pro/blogs/"
33
+ title="HumanizerX Blog"
34
+ className="w-full h-full min-h-screen border-0"
35
+ />
36
+ </main>
37
+
38
+ {/* ✅ Footer always at bottom */}
39
+ <Footer />
40
+ </div>
41
+ );
42
+ };
43
+
44
+ export default BlogPage;
src/pages/ContactPage.tsx ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useContext } from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { Mail, Phone, MapPin, Send, CheckCircle } from "lucide-react";
4
+ import { Helmet } from "react-helmet-async";
5
+ import Header from "../components/Header";
6
+ import Footer from "../components/Footer";
7
+ import { useForm, ValidationError } from "@formspree/react";
8
+ import { ThemeContext } from "../contexts/ThemeContext"; // ✅ use ThemeContext
9
+
10
+ const ContactPage: React.FC = () => {
11
+ const [state, handleSubmit] = useForm("xandyljo"); // Formspree form ID
12
+ const { theme } = useContext(ThemeContext); // ✅ get theme
13
+
14
+ return (
15
+ <div
16
+ className={`min-h-screen transition-colors duration-300 ${
17
+ theme === "dark" ? "bg-gray-900" : "bg-gray-50"
18
+ }`}
19
+ >
20
+ <Helmet>
21
+ <title>Contact HumanizerX – Get Support or Ask Questions</title>
22
+ <meta
23
+ name="description"
24
+ content="Get in touch with HumanizerX. Contact us for support, partnerships, or inquiries about our AI text humanization technology."
25
+ />
26
+ <link rel="canonical" href="https://www.humanizerx.pro/contact" />
27
+ </Helmet>
28
+
29
+ <Header currentPage="Contact" /> {/* optional: pass currentPage */}
30
+
31
+ {/* Hero Section */}
32
+ <section className="bg-gradient-to-r from-indigo-900 to-slate-900 text-white py-20">
33
+ <div className="container mx-auto px-4 text-center">
34
+ <img
35
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
36
+ alt="Humanizer X - AI Text Humanization Tool Logo"
37
+ title="Contact Humanizer X - Get in touch with our support team"
38
+ className="mx-auto w-40 h-30 object-contain mb-6"
39
+ />
40
+ <h1 className="text-5xl md:text-6xl font-bold leading-tight mb-4">
41
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
42
+ Contact
43
+ </span>{" "}
44
+ <span className="text-white">Us</span>
45
+ </h1>
46
+ <p className="text-lg md:text-xl max-w-3xl mx-auto text-blue-100">
47
+ Get in touch with Humanizer X. We're here to help you with inquiries, support, or partnerships.
48
+ </p>
49
+ </div>
50
+ </section>
51
+
52
+ <main className="container mx-auto px-4 py-8 max-w-6xl">
53
+ <div className="grid lg:grid-cols-2 gap-12">
54
+ {/* Contact Form */}
55
+ <div
56
+ className={`rounded-xl shadow-lg p-8 ${
57
+ theme === "dark" ? "bg-gray-800" : "bg-white"
58
+ }`}
59
+ >
60
+ <h2
61
+ className={`text-2xl font-bold mb-6 ${
62
+ theme === "dark" ? "text-white" : "text-gray-900"
63
+ }`}
64
+ >
65
+ Send us a Message
66
+ </h2>
67
+
68
+ <form onSubmit={handleSubmit} className="space-y-6">
69
+ <div className="grid md:grid-cols-2 gap-4">
70
+ <div>
71
+ <label
72
+ htmlFor="name"
73
+ className={`block text-sm font-medium mb-2 ${
74
+ theme === "dark" ? "text-gray-300" : "text-gray-700"
75
+ }`}
76
+ >
77
+ Full Name
78
+ </label>
79
+ <input
80
+ id="name"
81
+ type="text"
82
+ name="name"
83
+ required
84
+ className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
85
+ theme === "dark"
86
+ ? "bg-gray-700 border-gray-600 text-white"
87
+ : "bg-white border-gray-300 text-gray-900"
88
+ }`}
89
+ placeholder="Your full name"
90
+ />
91
+ <ValidationError prefix="Name" field="name" errors={state.errors} />
92
+ </div>
93
+
94
+ <div>
95
+ <label
96
+ htmlFor="email"
97
+ className={`block text-sm font-medium mb-2 ${
98
+ theme === "dark" ? "text-gray-300" : "text-gray-700"
99
+ }`}
100
+ >
101
+ Email Address
102
+ </label>
103
+ <input
104
+ id="email"
105
+ type="email"
106
+ name="email"
107
+ required
108
+ className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
109
+ theme === "dark"
110
+ ? "bg-gray-700 border-gray-600 text-white"
111
+ : "bg-white border-gray-300 text-gray-900"
112
+ }`}
113
+ placeholder="your@email.com"
114
+ />
115
+ <ValidationError prefix="Email" field="email" errors={state.errors} />
116
+ </div>
117
+ </div>
118
+
119
+ <div>
120
+ <label
121
+ htmlFor="subject"
122
+ className={`block text-sm font-medium mb-2 ${
123
+ theme === "dark" ? "text-gray-300" : "text-gray-700"
124
+ }`}
125
+ >
126
+ Subject
127
+ </label>
128
+ <input
129
+ id="subject"
130
+ type="text"
131
+ name="subject"
132
+ required
133
+ className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${
134
+ theme === "dark"
135
+ ? "bg-gray-700 border-gray-600 text-white"
136
+ : "bg-white border-gray-300 text-gray-900"
137
+ }`}
138
+ placeholder="How can we help you?"
139
+ />
140
+ <ValidationError prefix="Subject" field="subject" errors={state.errors} />
141
+ </div>
142
+
143
+ <div>
144
+ <label
145
+ htmlFor="message"
146
+ className={`block text-sm font-medium mb-2 ${
147
+ theme === "dark" ? "text-gray-300" : "text-gray-700"
148
+ }`}
149
+ >
150
+ Message
151
+ </label>
152
+ <textarea
153
+ id="message"
154
+ name="message"
155
+ required
156
+ rows={6}
157
+ className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none ${
158
+ theme === "dark"
159
+ ? "bg-gray-700 border-gray-600 text-white"
160
+ : "bg-white border-gray-300 text-gray-900"
161
+ }`}
162
+ placeholder="Tell us more about your inquiry..."
163
+ />
164
+ <ValidationError prefix="Message" field="message" errors={state.errors} />
165
+ </div>
166
+
167
+ <button
168
+ type="submit"
169
+ disabled={state.submitting || state.succeeded}
170
+ className="w-full px-6 py-3 bg-gradient-to-r from-indigo-900 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center gap-2"
171
+ >
172
+ {state.submitting ? (
173
+ <>
174
+ <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
175
+ Sending...
176
+ </>
177
+ ) : state.succeeded ? (
178
+ <>
179
+ <CheckCircle size={16} />
180
+ Sent!
181
+ </>
182
+ ) : (
183
+ <>
184
+ <Send size={16} />
185
+ Send Message
186
+ </>
187
+ )}
188
+ </button>
189
+
190
+ {state.succeeded && (
191
+ <div
192
+ className={`text-center mt-4 p-4 rounded-lg ${
193
+ theme === "dark"
194
+ ? "bg-green-900/30 text-green-400"
195
+ : "bg-green-100 text-green-600"
196
+ }`}
197
+ >
198
+ <CheckCircle
199
+ className={`mx-auto ${
200
+ theme === "dark" ? "text-green-400" : "text-green-600"
201
+ }`}
202
+ size={32}
203
+ />
204
+ <p className="mt-2 font-medium">
205
+ 🎉 Thank you! Your message has been received. We'll get back to you shortly.
206
+ </p>
207
+ </div>
208
+ )}
209
+ </form>
210
+ </div>
211
+
212
+ {/* Contact Info Section */}
213
+ <div className="space-y-6">
214
+ <div
215
+ className={`rounded-xl shadow-lg p-6 ${
216
+ theme === "dark" ? "bg-gray-800" : "bg-white"
217
+ }`}
218
+ >
219
+ <h3
220
+ className={`text-xl font-semibold mb-4 ${
221
+ theme === "dark" ? "text-white" : "text-gray-900"
222
+ }`}
223
+ >
224
+ Get in Touch
225
+ </h3>
226
+ <div className="space-y-4">
227
+ <div className="flex items-center gap-3">
228
+ <div
229
+ className={`w-10 h-10 rounded-lg flex items-center justify-center ${
230
+ theme === "dark" ? "bg-blue-900/30" : "bg-blue-100"
231
+ }`}
232
+ >
233
+ <Mail
234
+ className={theme === "dark" ? "text-blue-400" : "text-blue-600"}
235
+ size={20}
236
+ />
237
+ </div>
238
+ <div>
239
+ <div
240
+ className={`font-medium ${
241
+ theme === "dark" ? "text-white" : "text-gray-900"
242
+ }`}
243
+ >
244
+ Email
245
+ </div>
246
+ <div
247
+ className={theme === "dark" ? "text-gray-300" : "text-gray-600"}
248
+ >
249
+ humanizerx@gmail.com
250
+ </div>
251
+ </div>
252
+ </div>
253
+
254
+ <div className="flex items-center gap-3">
255
+ <div
256
+ className={`w-10 h-10 rounded-lg flex items-center justify-center ${
257
+ theme === "dark" ? "bg-green-900/30" : "bg-green-100"
258
+ }`}
259
+ >
260
+ <Phone
261
+ className={theme === "dark" ? "text-green-400" : "text-green-600"}
262
+ size={20}
263
+ />
264
+ </div>
265
+ <div>
266
+ <div
267
+ className={`font-medium ${
268
+ theme === "dark" ? "text-white" : "text-gray-900"
269
+ }`}
270
+ >
271
+ Phone
272
+ </div>
273
+ <div
274
+ className={theme === "dark" ? "text-gray-300" : "text-gray-600"}
275
+ >
276
+ (+92) 3143507460
277
+ </div>
278
+ </div>
279
+ </div>
280
+
281
+ <div className="flex items-center gap-3">
282
+ <div
283
+ className={`w-10 h-10 rounded-lg flex items-center justify-center ${
284
+ theme === "dark" ? "bg-purple-900/30" : "bg-purple-100"
285
+ }`}
286
+ >
287
+ <MapPin
288
+ className={theme === "dark" ? "text-purple-400" : "text-purple-600"}
289
+ size={20}
290
+ />
291
+ </div>
292
+ <div>
293
+ <div
294
+ className={`font-medium ${
295
+ theme === "dark" ? "text-white" : "text-gray-900"
296
+ }`}
297
+ >
298
+ Address
299
+ </div>
300
+ <div
301
+ className={theme === "dark" ? "text-gray-300" : "text-gray-600"}
302
+ >
303
+ 56130, Lahore, Punjab, Pakistan
304
+ </div>
305
+ </div>
306
+ </div>
307
+ </div>
308
+ </div>
309
+
310
+ {/* Business Hours */}
311
+ <div
312
+ className={`rounded-xl shadow-lg p-6 ${
313
+ theme === "dark" ? "bg-gray-800" : "bg-white"
314
+ }`}
315
+ >
316
+ <h3
317
+ className={`text-xl font-semibold mb-4 ${
318
+ theme === "dark" ? "text-white" : "text-gray-900"
319
+ }`}
320
+ >
321
+ Business Hours
322
+ </h3>
323
+ <div
324
+ className={`space-y-2 ${
325
+ theme === "dark" ? "text-gray-300" : "text-gray-600"
326
+ }`}
327
+ >
328
+ <div className="flex justify-between">
329
+ <span>Monday - Friday</span>
330
+ <span>9:00 AM - 6:00 PM PST</span>
331
+ </div>
332
+ <div className="flex justify-between">
333
+ <span>Saturday</span>
334
+ <span>10:00 AM - 4:00 PM PST</span>
335
+ </div>
336
+ <div className="flex justify-between">
337
+ <span>Sunday</span>
338
+ <span>Closed</span>
339
+ </div>
340
+ </div>
341
+ </div>
342
+
343
+ {/* Help Section */}
344
+ <div
345
+ className={`rounded-xl p-6 border ${
346
+ theme === "dark"
347
+ ? "from-blue-900/20 to-purple-900/20 bg-gradient-to-br border-blue-800"
348
+ : "from-blue-50 to-purple-50 bg-gradient-to-br border-blue-200"
349
+ }`}
350
+ >
351
+ <h3
352
+ className={`text-lg font-semibold mb-2 ${
353
+ theme === "dark" ? "text-white" : "text-gray-900"
354
+ }`}
355
+ >
356
+ Need Immediate Help?
357
+ </h3>
358
+ <p
359
+ className={`text-sm mb-4 ${
360
+ theme === "dark" ? "text-gray-300" : "text-gray-600"
361
+ }`}
362
+ >
363
+ Check out our comprehensive documentation and FAQ section for quick
364
+ answers to common questions.
365
+ </p>
366
+ <Link
367
+ to="/help-center"
368
+ className={`font-medium hover:underline ${
369
+ theme === "dark" ? "text-blue-400" : "text-blue-600"
370
+ }`}
371
+ >
372
+ Visit Help Center →
373
+ </Link>
374
+ </div>
375
+ </div>
376
+ </div>
377
+ </main>
378
+
379
+ <Footer />
380
+ </div>
381
+ );
382
+ };
383
+
384
+ export default ContactPage;
src/pages/DisclaimerPage.tsx ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/DisclaimerPage.tsx
2
+ import React, { useContext } from "react";
3
+ import { Helmet } from "react-helmet-async";
4
+ import Header from "../components/Header";
5
+ import Footer from "../components/Footer";
6
+ import { ThemeContext } from "../contexts/ThemeContext";
7
+ import { Link } from "react-router-dom";
8
+
9
+ const DisclaimerPage: React.FC = () => {
10
+ const { theme } = useContext(ThemeContext);
11
+
12
+ const textPrimary = theme === "dark" ? "text-white" : "text-gray-900";
13
+ const textSecondary = theme === "dark" ? "text-gray-300" : "text-gray-600";
14
+
15
+ return (
16
+ <div
17
+ className={`min-h-screen transition-colors duration-300 ${
18
+ theme === "dark" ? "bg-gray-900" : "bg-gray-50"
19
+ }`}
20
+ >
21
+ <Helmet>
22
+ <title>Disclaimer – HumanizerX Tools & Services</title>
23
+ <meta
24
+ name="description"
25
+ content="Read the official disclaimer of HumanizerX. Learn about our limitations of liability, external links policy, and user consent regarding our AI text humanization services."
26
+ />
27
+ <link rel="canonical" href="https://www.humanizerx.pro/disclaimer" />
28
+ </Helmet>
29
+ <Header />
30
+
31
+ <section className="py-16 mb-16">
32
+ <div className="container mx-auto px-4 max-w-6xl">
33
+ <div className={`${theme === "dark" ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"} rounded-xl shadow-lg border p-10 space-y-8`}>
34
+
35
+ {/* Logo and Header */}
36
+ <div className="text-center">
37
+ <img
38
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
39
+ alt="Humanizer X Disclaimer - Legal Disclaimer Information Logo"
40
+ title="Humanizer X Disclaimer - Important legal information and limitations"
41
+ className="mx-auto w-40 h-30 object-contain mb-6"
42
+ />
43
+ <h1 className={`text-4xl md:text-5xl font-bold mb-4 ${textPrimary}`}>Disclaimer</h1>
44
+ </div>
45
+
46
+ {/* Disclaimer Content */}
47
+ <div className="space-y-6">
48
+ <p className={`${textSecondary} text-lg md:text-xl`}>
49
+ If you require any more information or have any questions about our site's disclaimer, please feel free to contact us by email at{" "}
50
+ <a
51
+ href="https://mail.google.com/mail/?view=cm&fs=1&to=humanizerx@gmail.com"
52
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
53
+ >
54
+ humanizerx@gmail.com
55
+ </a>.
56
+ </p>
57
+
58
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Disclaimer for Humanizer X:</h2>
59
+ <p className={`${textSecondary} text-lg md:text-xl`}>
60
+ All the information on this website -{" "}
61
+ <a
62
+ href="https://www.humanizerx.pro/"
63
+ target="_blank"
64
+ rel="noopener noreferrer"
65
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
66
+ >
67
+ https://www.humanizerx.pro/
68
+ </a>{" "}
69
+ - is published in good faith and for general information purposes only. Humanizer X doesn't provide any warranties about the completeness or reliability of this information. Any action you take upon the information you find on this website is strictly at your own risk. Humanizer X will not be liable for any losses or damages in connection with the use of our website.
70
+ </p>
71
+
72
+ <p className={`${textSecondary} text-lg md:text-xl`}>
73
+ From our website, users can visit other websites via hyperlinks. While we strive to provide only useful and ethical links, we have no control over the content and nature of these sites. Links to other websites do not imply a recommendation for all the content found on these sites. Site owners and content may change without notice.
74
+ </p>
75
+
76
+ <p className={`${textSecondary} text-lg md:text-xl`}>
77
+ Please note that when you leave our website, other sites may have different privacy policies and terms beyond our control. Be sure to check the Privacy Policies and Terms of Service of these sites before engaging in any business or uploading any information.
78
+ </p>
79
+
80
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Consent:</h2>
81
+ <p className={`${textSecondary} text-lg md:text-xl`}>
82
+ By using our website, you hereby consent to our disclaimer and agree to its terms.
83
+ </p>
84
+
85
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Update:</h2>
86
+ <p className={`${textSecondary} text-lg md:text-xl`}>
87
+ Should we update, amend, or make any changes to this document, those changes will be prominently posted here.
88
+ </p>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </section>
93
+
94
+ <Footer />
95
+ </div>
96
+ );
97
+ };
98
+
99
+ export default DisclaimerPage;
src/pages/GPTChecker.tsx ADDED
@@ -0,0 +1,979 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useRef, useContext, useEffect } from 'react';
2
+ import {
3
+ Upload,
4
+ FileText,
5
+ AlertCircle,
6
+ CheckCircle,
7
+ Copy,
8
+ Download,
9
+ Brain,
10
+ FileSearch,
11
+ BarChart2,
12
+ Shield,
13
+ ChevronRight
14
+ } from 'lucide-react';
15
+ import { Helmet } from "react-helmet-async";
16
+ import { Link } from 'react-router-dom';
17
+ import { ThemeContext } from '../contexts/ThemeContext';
18
+ import ReactMarkdown from "react-markdown";
19
+ import remarkGfm from "remark-gfm";
20
+ import Header from '../components/Header';
21
+ import Footer from '../components/Footer';
22
+ import { auth, db } from '../firebase';
23
+ import { doc, updateDoc, increment, addDoc, getDoc, collection, Timestamp, onSnapshot } from 'firebase/firestore';
24
+
25
+ interface GPTCheckResult {
26
+ success: boolean;
27
+ ai_detected_percentage: number;
28
+ human_detected_percentage: number;
29
+ plagiarism_detected_percentage: number;
30
+ message: string;
31
+ total_words: number;
32
+ ai_sentences: string[];
33
+ human_sentences: string[];
34
+ analysis_date: string;
35
+ confidence_score: number;
36
+ accuracy_level: string;
37
+ }
38
+
39
+ const GPTChecker: React.FC = () => {
40
+ const [text, setText] = useState('');
41
+ const [result, setResult] = useState<GPTCheckResult | null>(null);
42
+ const [gptAverage, setGptAverage] = useState<number | null>(null);
43
+ const [loading, setLoading] = useState(false);
44
+ const [copied, setCopied] = useState(false);
45
+ const [error, setError] = useState('');
46
+ const [uploadedFileName, setUploadedFileName] = useState('');
47
+ const fileInputRef = useRef<HTMLInputElement>(null);
48
+ const { theme } = useContext(ThemeContext);
49
+
50
+ useEffect(() => {
51
+ if (!auth.currentUser) return;
52
+
53
+ const userRef = doc(db, "users", auth.currentUser.uid);
54
+ const unsubscribe = onSnapshot(userRef, (snap) => {
55
+ const stats = snap.data()?.stats;
56
+ const safeGptAvg = typeof stats?.gptAverage === 'number' && !isNaN(stats.gptAverage) ? stats.gptAverage : 0;
57
+ setGptAverage(safeGptAvg);
58
+ });
59
+
60
+ return () => unsubscribe();
61
+ }, []);
62
+
63
+
64
+ // --- Handlers ---
65
+ const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
66
+ const newText = e.target.value;
67
+ setText(newText);
68
+ if (result) setResult(null);
69
+ const wordCount = newText.trim().split(/\s+/).filter(Boolean).length;
70
+ if (wordCount <= 10000) setError('');
71
+ };
72
+
73
+ const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
74
+ const file = e.target.files?.[0];
75
+ if (!file) return;
76
+ setUploadedFileName(file.name);
77
+
78
+ if (file.type === 'text/plain') {
79
+ const content = await file.text();
80
+ setText(content);
81
+ setError('');
82
+ } else {
83
+ try {
84
+ const formData = new FormData();
85
+ formData.append('file', file);
86
+ setLoading(true);
87
+
88
+ const response = await fetch('https://humanizerx.pro/api/upload-file', {
89
+ method: 'POST',
90
+ body: formData,
91
+ });
92
+
93
+ const data = await response.json();
94
+ if (data.success) {
95
+ setText(data.extracted_text);
96
+ setUploadedFileName(data.file_name);
97
+ setError('');
98
+ } else {
99
+ setError(data.error || 'Failed to process file');
100
+ }
101
+ } catch {
102
+ setError('Error uploading file');
103
+ } finally {
104
+ setLoading(false);
105
+ }
106
+ }
107
+ };
108
+
109
+ const checkGPT = async () => {
110
+ if (!auth.currentUser) {
111
+ setError('Please log in to analyze text');
112
+ return;
113
+ }
114
+ if (!text.trim()) {
115
+ setError('Please enter text to analyze');
116
+ return;
117
+ }
118
+
119
+ const wordCount = text.trim().split(/\s+/).filter(Boolean).length;
120
+ if (wordCount > 10000) {
121
+ setError(`Word limit exceeded. Maximum 10,000 words allowed. Current: ${wordCount} words.`);
122
+ return;
123
+ }
124
+
125
+ try {
126
+ setLoading(true);
127
+ setError('');
128
+
129
+ // --- GPT Checker API Call ---
130
+ const response = await fetch('https://humanizerx.pro/api/detect-gpt', {
131
+ method: 'POST',
132
+ headers: { 'Content-Type': 'application/json' },
133
+ body: JSON.stringify({ text }),
134
+ });
135
+ const data: GPTCheckResult = await response.json();
136
+
137
+ // --- Replace your current `if (data.success) { ... }` block with this ---
138
+ if (data.success) {
139
+ // sanitize incoming API numbers
140
+ const aiPercentage = Number(data.ai_detected_percentage) || 0;
141
+ const humanPercentage = Math.max(0, Math.min(100, 100 - aiPercentage)); // clamp 0-100
142
+
143
+ // update UI with safe values
144
+ setResult({
145
+ ...data,
146
+ ai_detected_percentage: aiPercentage,
147
+ human_detected_percentage: humanPercentage,
148
+ });
149
+
150
+ // --- Firestore: running average for GPT Checker only ---
151
+ const userRef = doc(db, "users", auth.currentUser.uid);
152
+
153
+ // Fetch existing user stats
154
+ const userSnap = await getDoc(userRef);
155
+
156
+ let prevChecks = 0;
157
+ let prevAverage = 0;
158
+
159
+ if (userSnap.exists()) {
160
+ const stats = userSnap.data()?.stats || {};
161
+ prevChecks = Number(stats.gptChecks) || 0;
162
+ prevAverage = Number(stats.gptAverage);
163
+ if (isNaN(prevAverage)) prevAverage = 0;
164
+ }
165
+
166
+ // <<<< DEFINE newScore HERE >>>>
167
+ const newScore = humanPercentage; // <--- THIS fixes the ReferenceError
168
+
169
+ // Calculate new running average
170
+ const totalChecks = prevChecks + 1;
171
+ let newAverage =
172
+ totalChecks > 0
173
+ ? ((prevAverage * prevChecks) + newScore) / totalChecks
174
+ : newScore;
175
+
176
+ // ✅ Format to 2 decimals (13.40 instead of 13.4)
177
+ newAverage = Number(newAverage.toFixed(2));
178
+
179
+ // Persist only GPT stats (gptChecks + gptAverage)
180
+ await updateDoc(userRef, {
181
+ "stats.gptChecks": increment(1),
182
+ "stats.gptAverage": newAverage,
183
+ });
184
+ } else {
185
+ setError(data.message || 'Failed to analyze text');
186
+ }
187
+ } catch (err) {
188
+ console.error(err);
189
+ setError('Error analyzing text');
190
+ } finally {
191
+ setLoading(false);
192
+ }
193
+ };
194
+
195
+ const highlightAIText = (text: string, aiSentences: string[]) => {
196
+ let highlightedText = text;
197
+ aiSentences.forEach(sentence => {
198
+ const escaped = sentence.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
199
+ const regex = new RegExp(`(${escaped})`, 'gi');
200
+ highlightedText = highlightedText.replace(
201
+ regex,
202
+ '<span class="bg-red-200 dark:bg-red-800 px-1 rounded border border-red-400 dark:border-red-600" title="AI-generated content detected with high confidence">$1</span>'
203
+ );
204
+ });
205
+ return highlightedText;
206
+ };
207
+
208
+ const highlightHumanText = (text: string, humanSentences: string[]) => {
209
+ let highlightedText = text;
210
+ humanSentences.forEach(sentence => {
211
+ const escaped = sentence.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
212
+ const regex = new RegExp(`(${escaped})`, 'gi');
213
+ highlightedText = highlightedText.replace(
214
+ regex,
215
+ '<span class="bg-green-200 dark:bg-green-800 px-1 rounded border border-green-400 dark:border-green-600" title="Human-written content detected">$1</span>'
216
+ );
217
+ });
218
+ return highlightedText;
219
+ };
220
+
221
+ const getFullHighlightedText = (text: string, result: GPTCheckResult) => {
222
+ let highlighted = text;
223
+ highlighted = highlightAIText(highlighted, result.ai_sentences || []);
224
+ highlighted = highlightHumanText(highlighted, result.human_sentences || []);
225
+
226
+ // convert newlines to <br>
227
+ highlighted = highlighted.replace(/\n/g, "<br>");
228
+ return highlighted;
229
+ };
230
+
231
+ const copyToClipboard = () => {
232
+ navigator.clipboard.writeText(text);
233
+ setCopied(true);
234
+ setTimeout(() => setCopied(false), 2000); // reset after 2 seconds
235
+ };
236
+
237
+ const downloadReport = () => {
238
+ if (!result) return;
239
+ const report = `
240
+ GPT Detection Report
241
+ Generated: ${new Date(result.analysis_date).toLocaleString()}
242
+
243
+ Analysis Results:
244
+ - AI Detection: ${result.ai_detected_percentage}%
245
+ - Plagiarism Detection: ${result.plagiarism_detected_percentage}%
246
+ - Total Words: ${result.total_words}
247
+ - AI Sentences Detected: ${result.ai_sentences.length}
248
+
249
+ Summary: ${result.message}
250
+
251
+ Original Text:
252
+ ${text}
253
+ `.trim();
254
+
255
+ const blob = new Blob([report], { type: 'text/plain' });
256
+ const url = URL.createObjectURL(blob);
257
+ const a = document.createElement('a');
258
+ a.href = url;
259
+ a.download = `gpt-detection-report-${Date.now()}.txt`;
260
+ a.click();
261
+ URL.revokeObjectURL(url);
262
+ };
263
+
264
+ const wordCount = text.trim().split(/\s+/).filter(Boolean).length;
265
+
266
+ // --- Theme Classes ---
267
+ const bgMain = theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-gray-50 text-gray-900';
268
+ const cardBg = theme === 'dark' ? 'bg-gray-800' : 'bg-white';
269
+ const secondaryText = theme === 'dark' ? 'text-gray-300' : 'text-gray-600';
270
+ const borderCard = theme === 'dark' ? 'border-gray-700' : 'border-gray-200';
271
+
272
+ return (
273
+ <div className={`min-h-screen transition-colors duration-300 ${bgMain}`}>
274
+ {/* ✅ Canonical tags go right here */}
275
+ <Helmet>
276
+ <title>GPT Checker – Detect AI-Generated Text Instantly | HumanizerX</title>
277
+ <meta
278
+ name="description"
279
+ content="Use Humanizer X GPT Checker to detect AI-generated content with high accuracy. Ensure originality, clarity, and SEO compliance."
280
+ />
281
+ <link
282
+ rel="canonical"
283
+ href="https://www.humanizerx.pro/gpt-checker"
284
+ />
285
+ </Helmet>
286
+ <Header />
287
+
288
+ {/* Hero */}
289
+ <section className="bg-gradient-to-r from-indigo-900 to-slate-900 text-white py-20 text-center">
290
+ <div className="container mx-auto px-4 max-w-4xl">
291
+ <img
292
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
293
+ alt="Humanizer X GPT Checker - AI Content Detection Tool Logo"
294
+ title="Humanizer X GPT Checker - Detect AI-generated content with high accuracy"
295
+ className="mx-auto w-40 h-30 object-contain"
296
+ />
297
+ <h1 className="text-5xl md:text-6xl font-bold leading-tight mt-6">
298
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
299
+ GPT
300
+ </span> Checker
301
+ </h1>
302
+ <p className="text-xl md:text-2xl mt-7 mb-7 text-blue-100">
303
+ Advanced algorithms to identify AI-generated content with high accuracy and detailed analysis.
304
+ </p>
305
+ <div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
306
+ <div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-3">
307
+ <span className="text-lg font-semibold">Current Tool: </span>
308
+ <span className="text-blue-200 capitalize">GPT Checker</span>
309
+ </div>
310
+ <Link
311
+ to="/"
312
+ className="inline-flex items-center gap-2 bg-white text-purple-600 px-6 py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors"
313
+ >
314
+ <Shield size={20} />
315
+ Humanizer X
316
+ <ChevronRight size={16} />
317
+ </Link>
318
+ </div>
319
+ </div>
320
+ </section>
321
+
322
+ <div className="container mx-auto px-4 mt-10">
323
+ <div className="grid lg:grid-cols-2 gap-6">
324
+ {/* Input Section */}
325
+ <div className={`${cardBg} rounded-xl shadow-lg p-6 border ${borderCard} space-y-4`}>
326
+ {/* Label + Upload */}
327
+ <div className="flex items-center justify-between">
328
+ <h3 className={`text-lg font-semibold ${secondaryText}`}>Input Section</h3>
329
+ <div className="flex gap-2">
330
+ <label className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm rounded-lg cursor-pointer hover:bg-blue-700 transition-colors shadow-md">
331
+ <Upload size={18} />
332
+ <span>Upload</span>
333
+ <input
334
+ type="file"
335
+ accept=".txt,.docx"
336
+ onChange={handleFileUpload}
337
+ className="hidden"
338
+ />
339
+ </label>
340
+ </div>
341
+ </div>
342
+
343
+
344
+ {/* Textarea */}
345
+ <div className={`relative h-96 border rounded-xl shadow-inner transition-all duration-200 ${
346
+ theme === "dark"
347
+ ? "bg-gradient-to-br from-gray-800 to-gray-900 border-gray-700 text-white"
348
+ : "bg-gradient-to-br from-gray-50 to-white border-gray-300 text-gray-900"
349
+ }`}>
350
+ <textarea
351
+ value={text}
352
+ onChange={handleTextChange}
353
+ placeholder="Paste text here or upload a file..."
354
+ className="w-full h-full p-4 resize-none bg-transparent focus:ring-2 focus:ring-blue-500 focus:border-transparent rounded-xl"
355
+ style={{
356
+ lineHeight: "1.6",
357
+ fontSize: "0.95rem",
358
+ fontFamily: "Inter, sans-serif",
359
+ }}
360
+ />
361
+ {uploadedFileName && (
362
+ <p className={`absolute bottom-2 left-4 text-xs ${secondaryText} truncate max-w-[70%]`}>
363
+ Uploaded: {uploadedFileName}
364
+ </p>
365
+ )}
366
+ </div>
367
+
368
+ {/* Word count + button */}
369
+ <div className="flex justify-between items-center">
370
+ <span className={`text-sm ${wordCount > 10000 ? (theme === "dark" ? "text-red-400" : "text-red-600") : secondaryText}`}>
371
+ {wordCount.toLocaleString()} / 10,000 words
372
+ </span>
373
+ <button
374
+ onClick={checkGPT}
375
+ disabled={loading || !text.trim() || wordCount > 10000}
376
+ className="px-6 py-3 bg-gradient-to-r from-indigo-900 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 disabled:opacity-50 transition-all duration-200 flex items-center gap-2"
377
+ >
378
+ {loading ? "Analyzing..." : "Check for AI Content"}
379
+ </button>
380
+ </div>
381
+
382
+ {/* Error */}
383
+ {error && (
384
+ <div className={`p-3 border rounded-lg ${theme === "dark" ? "bg-red-900/20 border-red-800 text-red-300" : "bg-red-50 border-red-200 text-red-700"}`}>
385
+ <AlertCircle size={16} className="inline mr-2" />
386
+ {error}
387
+ </div>
388
+ )}
389
+ </div>
390
+
391
+ {/* Output Section */}
392
+ <div className={`${cardBg} rounded-xl shadow-lg p-6 border ${borderCard} space-y-4`}>
393
+ <div className="flex items-center justify-between">
394
+ <h3 className={`text-lg font-semibold ${secondaryText}`}>Analysis Results</h3>
395
+ <div className="flex gap-2">
396
+ {text && (
397
+ <>
398
+ <button
399
+ onClick={copyToClipboard}
400
+ className={`flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-300 ${
401
+ copied
402
+ ? 'bg-green-600 text-white animate-pulse'
403
+ : 'bg-gray-600 text-white hover:bg-gray-700'
404
+ }`}
405
+ >
406
+ <Copy size={16} /> {copied ? "Copied!" : "Copy"}
407
+ </button>
408
+ <button
409
+ onClick={downloadReport}
410
+ className="flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
411
+ >
412
+ <Download size={16} /> Download
413
+ </button>
414
+ </>
415
+ )}
416
+ </div>
417
+ </div>
418
+
419
+ {!result ? (
420
+ <div className={`flex flex-col items-center justify-center h-96 ${secondaryText}`}>
421
+ <p>Enter text and click "Check for AI Content" to see results</p>
422
+ </div>
423
+ ) : (
424
+ <div className={`w-full h-96 p-4 border rounded-xl overflow-y-auto shadow-inner text-sm leading-relaxed ${
425
+ theme === "dark"
426
+ ? "bg-gradient-to-br from-gray-800 to-gray-900 border-gray-700 text-gray-100"
427
+ : "bg-gradient-to-br from-gray-50 to-white border-gray-300 text-gray-800"
428
+ }`}>
429
+ <div dangerouslySetInnerHTML={{ __html: getFullHighlightedText(text, result) }} />
430
+ </div>
431
+ )}
432
+ </div>
433
+ </div>
434
+
435
+ {/* Results Summary Cards */}
436
+ {result && (
437
+ <section className="mt-10 mb-10 container mx-auto px-4">
438
+ <div className={`${cardBg} rounded-xl shadow-lg border ${borderCard} p-6`}>
439
+ <h2 className="text-xl font-bold mb-6">Analysis Results</h2>
440
+
441
+ {/* Percentage Cards */}
442
+ <div className="grid md:grid-cols-3 gap-6 mb-6">
443
+ {/* AI Detection Card */}
444
+ <div className={`p-6 rounded-xl border-2 ${
445
+ result.ai_detected_percentage > 70
446
+ ? 'border-red-500 bg-red-50 dark:bg-red-900/20'
447
+ : result.ai_detected_percentage > 30
448
+ ? 'border-yellow-500 bg-yellow-50 dark:bg-yellow-900/20'
449
+ : 'border-green-500 bg-green-50 dark:bg-green-900/20'
450
+ }`}>
451
+ <div className="text-center">
452
+ <div className={`text-4xl font-bold mb-2 ${
453
+ result.ai_detected_percentage > 70
454
+ ? 'text-red-600 dark:text-red-400'
455
+ : result.ai_detected_percentage > 30
456
+ ? 'text-yellow-600 dark:text-yellow-400'
457
+ : 'text-green-600 dark:text-green-400'
458
+ }`}>
459
+ {result.ai_detected_percentage}%
460
+ </div>
461
+ <div className="text-sm font-medium text-gray-700 dark:text-gray-300">
462
+ AI Content Detected
463
+ </div>
464
+ <div className={`text-xs mt-1 ${
465
+ result.ai_detected_percentage > 70
466
+ ? 'text-red-500'
467
+ : result.ai_detected_percentage > 30
468
+ ? 'text-yellow-500'
469
+ : 'text-green-500'
470
+ }`}>
471
+ {result.ai_detected_percentage > 70
472
+ ? '❌ High AI Content'
473
+ : result.ai_detected_percentage > 30
474
+ ? '⚠️ Moderate AI Content'
475
+ : '✅ Low AI Content'
476
+ }
477
+ </div>
478
+ </div>
479
+ </div>
480
+
481
+ {/* Human Detection Card */}
482
+ <div className={`p-6 rounded-xl border-2 ${
483
+ (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 70
484
+ ? 'border-green-500 bg-green-50 dark:bg-green-900/20'
485
+ : (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 30
486
+ ? 'border-yellow-500 bg-yellow-50 dark:bg-yellow-900/20'
487
+ : 'border-red-500 bg-red-50 dark:bg-red-900/20'
488
+ }`}>
489
+ <div className="text-center">
490
+ <div className={`text-4xl font-bold mb-2 ${
491
+ (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 70
492
+ ? 'text-green-600 dark:text-green-400'
493
+ : (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 30
494
+ ? 'text-yellow-600 dark:text-yellow-400'
495
+ : 'text-red-600 dark:text-red-400'
496
+ }`}>
497
+ {result.human_detected_percentage || (100 - result.ai_detected_percentage)}%
498
+ </div>
499
+ <div className="text-sm font-medium text-gray-700 dark:text-gray-300">
500
+ Human Content Detected
501
+ </div>
502
+ <div className={`text-xs mt-1 ${
503
+ (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 70
504
+ ? 'text-green-500'
505
+ : (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 30
506
+ ? 'text-yellow-500'
507
+ : 'text-red-500'
508
+ }`}>
509
+ {(result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 70
510
+ ? '✅ Mostly Human'
511
+ : (result.human_detected_percentage || (100 - result.ai_detected_percentage)) > 30
512
+ ? '⚠️ Mixed Content'
513
+ : '❌ Mostly AI'
514
+ }
515
+ </div>
516
+ </div>
517
+ </div>
518
+
519
+ {/* Accuracy Card */}
520
+ <div className="p-6 rounded-xl border-2 border-blue-500 bg-blue-50 dark:bg-blue-900/20">
521
+ <div className="text-center">
522
+ <div className="text-4xl font-bold mb-2 text-blue-600 dark:text-blue-400">
523
+ {result.confidence_score || 95}%
524
+ </div>
525
+ <div className="text-sm font-medium text-gray-700 dark:text-gray-300">
526
+ Detection Accuracy
527
+ </div>
528
+ <div className="text-xs mt-1 text-blue-500">
529
+ {result.accuracy_level || 'High Confidence'}
530
+ </div>
531
+ </div>
532
+ </div>
533
+ </div>
534
+
535
+ {/* Detailed Analysis */}
536
+ <div className="grid md:grid-cols-2 gap-6">
537
+ <div className={`p-4 rounded-lg ${theme === 'dark' ? 'bg-gray-700' : 'bg-gray-100'}`}>
538
+ <h3 className="font-semibold mb-2">Analysis Summary</h3>
539
+ <div className="space-y-2 text-sm">
540
+ <div className="flex justify-between">
541
+ <span>Total Words:</span>
542
+ <span className="font-medium">{result.total_words?.toLocaleString()}</span>
543
+ </div>
544
+ <div className="flex justify-between">
545
+ <span>AI Sentences:</span>
546
+ <span className="font-medium text-red-600 dark:text-red-400">
547
+ {result.ai_sentences?.length || 0}
548
+ </span>
549
+ </div>
550
+ <div className="flex justify-between">
551
+ <span>Human Sentences:</span>
552
+ <span className="font-medium text-green-600 dark:text-green-400">
553
+ {result.human_sentences?.length || 0}
554
+ </span>
555
+ </div>
556
+ <div className="flex justify-between">
557
+ <span>Plagiarism Risk:</span>
558
+ <span className={`font-medium ${
559
+ result.plagiarism_detected_percentage > 20
560
+ ? 'text-red-600 dark:text-red-400'
561
+ : 'text-green-600 dark:text-green-400'
562
+ }`}>
563
+ {result.plagiarism_detected_percentage}%
564
+ </span>
565
+ </div>
566
+ </div>
567
+ </div>
568
+
569
+ <div className={`p-4 rounded-lg ${theme === 'dark' ? 'bg-gray-700' : 'bg-gray-100'}`}>
570
+ <h3 className="font-semibold mb-2">Legend</h3>
571
+ <div className="space-y-2 text-sm">
572
+ <div className="flex items-center gap-2">
573
+ <div className="w-4 h-4 bg-red-200 dark:bg-red-800 border border-red-400 dark:border-red-600 rounded"></div>
574
+ <span>AI-Generated Content</span>
575
+ </div>
576
+ <div className="flex items-center gap-2">
577
+ <div className="w-4 h-4 bg-green-200 dark:bg-green-800 border border-green-400 dark:border-green-600 rounded"></div>
578
+ <span>Human-Written Content</span>
579
+ </div>
580
+ <div className="text-xs text-gray-500 dark:text-gray-400 mt-2">
581
+ 💡 Hover over highlighted text for more details
582
+ </div>
583
+ </div>
584
+ </div>
585
+ </div>
586
+ </div>
587
+ </section>
588
+ )}
589
+ </div>
590
+ {gptAverage !== null && (
591
+ <section className="mt-10 mb-10 container mx-auto px-4">
592
+ <div className={`${cardBg} rounded-xl shadow-lg border ${borderCard} p-6`}>
593
+ <h2 className="text-xl font-bold mb-4">Your GPT Average</h2>
594
+
595
+ <div className="relative w-full bg-gray-200 dark:bg-gray-700 h-4 rounded-full overflow-hidden">
596
+ {/* Progress fill */}
597
+ <div
598
+ className="h-full transition-all duration-500"
599
+ style={{
600
+ width: `${gptAverage}%`,
601
+ backgroundColor:
602
+ gptAverage <= 30
603
+ ? "#22c55e" // green
604
+ : gptAverage <= 70
605
+ ? "#eab308" // yellow
606
+ : "#ef4444", // red
607
+ }}
608
+ ></div>
609
+
610
+ {/* Percentage text on right */}
611
+ <span
612
+ className="absolute right-0 top-0 -mt-6 text-sm font-medium"
613
+ style={{ color: gptAverage > 70 ? "#ef4444" : gptAverage > 30 ? "#eab308" : "#22c55e" }}
614
+ >
615
+ {gptAverage}%
616
+ </span>
617
+ </div>
618
+
619
+ <p className="mt-3 text-sm">
620
+ {gptAverage <= 30 && "✅ Low AI (Good)"}
621
+ {gptAverage > 30 && gptAverage <= 70 && "⚠️ Moderate AI"}
622
+ {gptAverage > 70 && "❌ High AI (Needs Humanizer)"}
623
+ </p>
624
+ </div>
625
+ </section>
626
+ )}
627
+
628
+ <FeatureCards theme={theme} />
629
+
630
+ {/* --- Humanizer X GPT Checker Full Article Section --- */}
631
+ <section className="py-16 mb-16">
632
+ <div className="container mx-auto px-4">
633
+ <div
634
+ className={`${
635
+ theme === "dark"
636
+ ? "bg-gray-800 border-gray-700"
637
+ : "bg-white border-gray-300"
638
+ } rounded-xl shadow-lg border p-10 space-y-8 transform scale-100 transition-transform duration-300`}
639
+ >
640
+ {/* Title */}
641
+ <h2 className="text-5xl md:text-5xl font-bold text-gray-900 dark:text-gray-100">
642
+ GPT Checker for Human-Like AI Content – Humanizer X
643
+ </h2>
644
+
645
+ {/* Intro */}
646
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
647
+ Introduction to Humanizer X GPT Checker:
648
+ </h3>
649
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
650
+ In today’s digital world, AI tools are essential for writers, students,
651
+ and marketers. The real challenge is ensuring AI-generated text sounds
652
+ natural and original. Without adjustments, AI writing often feels
653
+ robotic and flat. Humanizer X GPT Checker solves this by refining raw AI
654
+ outputs into polished, human-style content that flows smoothly and
655
+ maintains originality.
656
+ </p>
657
+
658
+ {/* Why It Matters */}
659
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
660
+ Why You Need Humanizer X GPT Checker?
661
+ </h3>
662
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
663
+ Online content succeeds only when it connects with readers. Search
664
+ engines reward human-preferred writing, while robotic text risks lower
665
+ rankings. A GPT checker with humanizer features ensures your writing
666
+ follows natural patterns, strengthens trust, and keeps your brand voice
667
+ consistent. It bridges the gap between AI assistance and genuine human
668
+ communication.
669
+ </p>
670
+
671
+ {/* Tool Overview */}
672
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
673
+ Humanizer X – A Powerful AI Humanizer:
674
+ </h3>
675
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
676
+ <a
677
+ href="https://www.humanizerx.pro"
678
+ className="text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200 underline"
679
+ target="_blank"
680
+ rel="noopener noreferrer"
681
+ >
682
+ Humanizer X
683
+ </a>{" "}
684
+ is built for anyone who uses AI but prefers natural writing. Instead of
685
+ rewriting text manually, our tool automatically adjusts word flow,
686
+ grammar, and tone. Whether you are a blogger, student, or business
687
+ professional, Humanizer X helps you create clear, human-like writing
688
+ that saves time and supports SEO.
689
+ </p>
690
+
691
+ {/* Benefits */}
692
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
693
+ Benefits of Using Humanizer X GPT Checker:
694
+ </h3>
695
+ <ul className="list-disc list-inside space-y-2 text-lg md:text-xl text-gray-800 dark:text-gray-300">
696
+ <li>Improves readability with smooth, natural text flow.</li>
697
+ <li>Reduces AI detection risks by refining robotic patterns.</li>
698
+ <li>Saves editing time for professionals and students.</li>
699
+ <li>Boosts SEO rankings with proper variation and clarity.</li>
700
+ <li>Delivers content that feels original and reader-friendly.</li>
701
+ </ul>
702
+
703
+ {/* Students */}
704
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
705
+ Humanizer X for Students and Researchers:
706
+ </h3>
707
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
708
+ Academic writing often suffers when AI tools produce robotic phrasing.
709
+ With Humanizer X GPT Checker, assignments, essays, and research papers
710
+ gain clarity, flow, and originality. This ensures work passes plagiarism
711
+ checks, sounds natural, and maintains academic credibility.
712
+ </p>
713
+
714
+ {/* Bloggers */}
715
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
716
+ Bloggers and Marketers Rely on Humanizer X:
717
+ </h3>
718
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
719
+ Bloggers and marketers need content that is engaging and unique. While
720
+ AI can generate drafts quickly, it often lacks personality. Humanizer X
721
+ transforms this into natural articles, ensuring SEO compliance and
722
+ building reader trust.
723
+ </p>
724
+
725
+ {/* Businesses */}
726
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
727
+ AI Text Converter for Business Documents:
728
+ </h3>
729
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
730
+ Businesses depend on clear, professional writing. Reports, proposals,
731
+ and marketing materials benefit from Humanizer X by eliminating robotic
732
+ phrasing and confirming originality. This strengthens credibility and
733
+ saves valuable time.
734
+ </p>
735
+
736
+ {/* SEO */}
737
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
738
+ SEO Advantages of Humanizer X:
739
+ </h3>
740
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
741
+ Search engines prioritize natural and structured content. Humanizer X
742
+ ensures keyword balance while avoiding repetitive AI patterns, leading
743
+ to stronger rankings and improved visibility for creators and brands.
744
+ </p>
745
+
746
+ {/* Social Media */}
747
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
748
+ Humanizer X for Social Media Success:
749
+ </h3>
750
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
751
+ Social platforms demand short, conversational, and relatable content.
752
+ Humanizer X refines captions, posts, and ads to feel authentic while
753
+ keeping them concise and impactful with word counter support.
754
+ </p>
755
+
756
+ {/* AI Detection */}
757
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
758
+ How GPT Checker Helps Avoid AI Detection?
759
+ </h3>
760
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
761
+ AI detectors scan for patterns of machine writing. GPT Checker smooths
762
+ unnatural sequences, replacing them with original alternatives. This
763
+ helps ensure academic, business, and creative content passes detection
764
+ tools confidently.
765
+ </p>
766
+
767
+ {/* Academic */}
768
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
769
+ Humanizer X in Academic Institutions:
770
+ </h3>
771
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
772
+ Many universities strictly limit unedited AI content. Humanizer X helps
773
+ students and researchers produce human-like writing that avoids AI
774
+ detection, ensuring credibility while allowing them to focus on
775
+ research, creativity, and ideas.
776
+ </p>
777
+
778
+ {/* Businesses */}
779
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
780
+ Why Businesses Trust Humanizer X Tools?
781
+ </h3>
782
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
783
+ Companies value professional communication. Humanizer X ensures emails,
784
+ reports, and marketing materials are polished, original, and natural.
785
+ GPT Checker verifies clarity and credibility, strengthening brand
786
+ reputation.
787
+ </p>
788
+
789
+ {/* Future */}
790
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
791
+ The Future of AI Humanizer Tools:
792
+ </h3>
793
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
794
+ As AI grows more advanced, the demand for humanized content increases.
795
+ GPT Checker tools will continue to evolve, balancing AI speed with
796
+ authentic human tone, offering better flexibility and resistance against
797
+ detection.
798
+ </p>
799
+
800
+ {/* Conclusion */}
801
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
802
+ Conclusion – Humanizer X GPT Checker for Better Writing:
803
+ </h3>
804
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
805
+ In conclusion,{" "}
806
+ <a
807
+ href="https://www.humanizerx.pro"
808
+ className="text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200 underline"
809
+ target="_blank"
810
+ rel="noopener noreferrer"
811
+ >
812
+ Humanizer X
813
+ </a>{" "}
814
+ is more than a GPT checker — it’s an AI text converter, humanizer, and
815
+ SEO-friendly writing assistant. Whether for blogs, academics, or
816
+ business, it helps you save time while ensuring originality, clarity,
817
+ and credibility.
818
+ </p>
819
+ </div>
820
+ </div>
821
+ </section>
822
+
823
+
824
+
825
+
826
+ {/* CTA */}
827
+ <section className="py-16 bg-gradient-to-r from-indigo-900 to-slate-900 text-white">
828
+ <div className="container mx-auto px-4 text-center">
829
+ <h2 className="text-3xl font-bold mb-4">Let Humanizer X Perfect Your Content</h2>
830
+ <p className="text-xl mb-8 text-blue-100">
831
+ Humanizer X refines AI text into authentic, human-like writing with built-in originality checks.
832
+ </p>
833
+ <Link
834
+ to="/"
835
+ className="inline-flex items-center gap-2 bg-white text-indigo-600 px-8 py-4 rounded-lg font-semibold text-lg hover:bg-gray-100 transition-colors"
836
+ >
837
+ <Shield size={24} />
838
+ Try Humanizer X
839
+ <ChevronRight size={20} />
840
+ </Link>
841
+ </div>
842
+ </section>
843
+
844
+ <Footer />
845
+ </div>
846
+ );
847
+ };
848
+
849
+ // --- Subcomponents ---
850
+ const ResultsSection = ({ result, text, downloadReport, highlightAIText, theme }: any) => (
851
+ <div className="space-y-6">
852
+ <div className="grid grid-cols-2 gap-4">
853
+ <ScoreCard title="AI Detection" value={result.ai_detected_percentage} color="red" icon={<AlertCircle size={20} />} theme={theme} />
854
+ <ScoreCard title="Plagiarism" value={result.plagiarism_detected_percentage} color="orange" icon={<CheckCircle size={20} />} theme={theme} />
855
+ </div>
856
+ <SummaryCard message={result.message} total_words={result.total_words} theme={theme} />
857
+ {result.ai_sentences.length > 0 && <HighlightedText text={text} ai_sentences={result.ai_sentences} highlightAIText={highlightAIText} theme={theme} />}
858
+ <Recommendations result={result} theme={theme} />
859
+ <div className="flex justify-end">
860
+ <button onClick={downloadReport} className="flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors">
861
+ <Download size={16} /> Download Report
862
+ </button>
863
+ </div>
864
+ </div>
865
+ );
866
+
867
+ const EmptyResults = ({ loading, theme }: any) => (
868
+ <div className={`flex flex-col items-center justify-center h-96 ${theme === 'dark' ? 'text-gray-400' : 'text-gray-500'}`}>
869
+ {loading ? (
870
+ <>
871
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-600 mb-4"></div>
872
+ <p className={`${theme === 'dark' ? 'text-gray-400' : 'text-gray-600'}`}>Analyzing content...</p>
873
+ </>
874
+ ) : (
875
+ <>
876
+ <FileText size={64} className="mb-4 opacity-50" />
877
+ <p className="text-lg">Enter text and click "Check for AI Content" to see results</p>
878
+ </>
879
+ )}
880
+ </div>
881
+ );
882
+
883
+ const ScoreCard = ({ icon, title, value, color, theme }: any) => {
884
+ const bgColor = theme === 'dark' ? `bg-${color}-900/20` : `bg-${color}-50`;
885
+ const borderColor = theme === 'dark' ? `border-${color}-800` : `border-${color}-200`;
886
+ const textColor = theme === 'dark' ? `text-${color}-200` : `text-${color}-800`;
887
+ const valueColor = theme === 'dark' ? `text-${color}-400` : `text-${color}-600`;
888
+ return (
889
+ <div className={`p-4 rounded-lg border ${borderColor} ${bgColor}`}>
890
+ <div className="flex items-center gap-2 mb-2 font-bold">{icon} <span className={textColor}>{title}</span></div>
891
+ <div className={`text-3xl font-bold ${valueColor}`}>{value}%</div>
892
+ </div>
893
+ );
894
+ };
895
+
896
+ const SummaryCard = ({ message, total_words, theme }: any) => {
897
+ const bgColor = theme === 'dark' ? 'bg-gray-700' : 'bg-gray-50';
898
+ const textColor = theme === 'dark' ? 'text-gray-300' : 'text-gray-700';
899
+ const headingColor = theme === 'dark' ? 'text-white' : 'text-gray-900';
900
+ return (
901
+ <div className={`${bgColor} p-4 rounded-lg`}>
902
+ <h3 className={`font-medium ${headingColor} mb-2`}>Analysis Summary</h3>
903
+ <p className={textColor}>{message}</p>
904
+ <div className={`mt-2 text-sm ${textColor}`}>Total words analyzed: {total_words.toLocaleString()}</div>
905
+ </div>
906
+ );
907
+ };
908
+
909
+ const HighlightedText = ({ text, ai_sentences, highlightAIText, theme }: any) => {
910
+ const bgColor = theme === 'dark' ? 'bg-gray-700' : 'bg-gray-50';
911
+ const headingColor = theme === 'dark' ? 'text-white' : 'text-gray-900';
912
+ const textColor = theme === 'dark' ? 'text-gray-200' : 'text-gray-800';
913
+ return (
914
+ <div className={`${bgColor} p-4 rounded-lg`}>
915
+ <h3 className={`${headingColor} font-medium mb-3`}>Text with AI Content Highlighted</h3>
916
+ <div className={`prose prose-sm max-w-none leading-relaxed ${textColor}`} dangerouslySetInnerHTML={{ __html: highlightAIText(text, ai_sentences) }} />
917
+ <div className={`mt-3 text-xs ${theme === 'dark' ? 'text-gray-400' : 'text-gray-500'}`}>💡 Purple highlighted sections indicate potential AI-generated content</div>
918
+ </div>
919
+ );
920
+ };
921
+
922
+ const Recommendations = ({ result, theme }: any) => {
923
+ const bgColor = theme === 'dark' ? 'bg-blue-900/20 border-blue-800 text-blue-300' : 'bg-blue-50 border-blue-200 text-blue-800';
924
+ return (
925
+ <div className={`p-4 rounded-lg border ${bgColor}`}>
926
+ <h3 className="font-medium mb-2">Recommendations</h3>
927
+ <ul className="text-sm space-y-1">
928
+ {result.ai_detected_percentage > 70 && <li>• Consider using our Humanizer tool to reduce AI detection</li>}
929
+ {result.plagiarism_detected_percentage > 20 && <li>• Review and rewrite sections that may be plagiarized</li>}
930
+ {result.ai_detected_percentage < 30 && result.plagiarism_detected_percentage < 15 && <li>• Great! Your content appears to be human-written and original</li>}
931
+ <li>• Always review and edit content for accuracy and context</li>
932
+ </ul>
933
+ </div>
934
+ );
935
+ };
936
+
937
+ const FeatureCards = ({ theme }: any) => (
938
+ <section className="mt-20 py-16">
939
+ <div className="container mx-auto px-4">
940
+ <div className={`${theme === 'dark' ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'} rounded-xl shadow-lg border p-8`}>
941
+ <div className="text-center mb-12">
942
+ <h2 className={`text-3xl font-bold mb-4 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>GPT Checker Features</h2>
943
+ <p className={`text-lg max-w-2xl mx-auto ${theme === 'dark' ? 'text-gray-300' : 'text-gray-600'}`}>
944
+ Advanced tools to verify, analyze, and ensure content authenticity
945
+ </p>
946
+ </div>
947
+ <div
948
+ className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
949
+ <FeatureCard icon={<Brain size={24} />} title="AI Detection" description="Accurately identifies AI-generated text using advanced algorithms." bgColor="blue" theme={theme} />
950
+ <FeatureCard icon={<FileSearch size={24} />} title="Plagiarism Scan" description="Checks against millions of sources to ensure originality." bgColor="purple" theme={theme} />
951
+ <FeatureCard icon={<BarChart2 size={24} />} title="Confidence Scoring" description="Provides a percentage score of how likely text is AI-generated." bgColor="green" theme={theme} />
952
+ <FeatureCard icon={<FileText size={24} />} title="Multi-Format Support" description="Upload text, PDF, or Word files for instant analysis." bgColor="orange" theme={theme} />
953
+ </div>
954
+ </div>
955
+ </div>
956
+ </section>
957
+ );
958
+
959
+ const FeatureCard = ({ icon, title, description, bgColor, theme }: any) => {
960
+ const colors: any = {
961
+ blue: ['bg-blue-100', 'dark:bg-blue-900/30', 'text-blue-600', 'dark:text-blue-400'],
962
+ purple: ['bg-purple-100', 'dark:bg-purple-900/30', 'text-purple-600', 'dark:text-purple-400'],
963
+ green: ['bg-green-100', 'dark:bg-green-900/30', 'text-green-600', 'dark:text-green-400'],
964
+ orange: ['bg-orange-100', 'dark:bg-orange-900/30', 'text-orange-600', 'dark:text-orange-400'],
965
+ };
966
+ const [bgLight, bgDark, textLight, textDark] = colors[bgColor];
967
+
968
+ return (
969
+ <div className={`${theme === 'dark' ? 'bg-gray-700' : 'bg-gray-50'} p-6 rounded-xl`}>
970
+ <div className={`w-12 h-12 ${theme === 'dark' ? bgDark : bgLight} rounded-lg flex items-center justify-center mb-4`}>
971
+ {React.cloneElement(icon, { className: theme === 'dark' ? textDark : textLight })}
972
+ </div>
973
+ <h3 className={`text-lg font-semibold mb-2 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>{title}</h3>
974
+ <p className={`text-sm ${theme === 'dark' ? 'text-gray-300' : 'text-gray-600'}`}>{description}</p>
975
+ </div>
976
+ );
977
+ };
978
+
979
+ export default GPTChecker;
src/pages/HelpCenterPage.tsx ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useContext, useState, useMemo } from "react";
2
+ import { Link } from "react-router-dom";
3
+ import { ThemeContext } from "../contexts/ThemeContext";
4
+ import Header from "../components/Header";
5
+ import Footer from "../components/Footer";
6
+ import { FiSearch } from "react-icons/fi";
7
+ import { Helmet } from "react-helmet-async";
8
+
9
+ interface FAQItem {
10
+ question: string;
11
+ answer: React.ReactNode;
12
+ }
13
+
14
+ const HelpCenterPage: React.FC = () => {
15
+ const { theme } = useContext(ThemeContext);
16
+ const [openIndex, setOpenIndex] = useState<number | null>(null);
17
+ const [searchTerm, setSearchTerm] = useState("");
18
+ const [inputValue, setInputValue] = useState("");
19
+
20
+ const toggleIndex = (index: number) => setOpenIndex(openIndex === index ? null : index);
21
+
22
+ const bgPage = theme === "dark" ? "bg-gray-900" : "bg-gray-50";
23
+ const bgCard = theme === "dark" ? "bg-gray-800" : "bg-white";
24
+ const textPrimary = theme === "dark" ? "text-white" : "text-gray-900";
25
+ const textSecondary = theme === "dark" ? "text-gray-300" : "text-gray-600";
26
+
27
+ const tools = [
28
+ { name: "Text Humanizer ✍️", path: "/", description: "Transform AI text into natural, human-like content." },
29
+ { name: "GPT Checker 🕵️‍♂️", path: "/gpt-checker", description: "Detect AI-generated text with high accuracy." },
30
+ { name: "Word Counter 🔢", path: "/word-counter", description: "Count words, characters, and readability stats." }
31
+ ];
32
+
33
+ const faqs: { category: string; items: FAQItem[] }[] = [
34
+ {
35
+ category: "Text Humanizer",
36
+ items: [
37
+ { question: "✍️ How do I humanize AI text?", answer: <>Go to <Link to="/" className="text-indigo-600 hover:underline">Text Humanizer</Link>, paste your text, choose a tone, and click “Humanize”.</> },
38
+ { question: "🎨 Can I customize tone & style?", answer: <>Yes, choose the tone, style, and audience for personalized output.</> },
39
+ { question: "📄 Can I humanize long documents?", answer: <>Yes, split large texts for best results.</> },
40
+ { question: "📑 Can I humanize multiple texts at once?", answer: <>Only one text at a time is supported for optimal quality.</> },
41
+ { question: "🌐 Does it support multiple languages?", answer: <>Currently, English is supported. More languages will be added soon.</> },
42
+ { question: "📝 Can I humanize bullet points?", answer: <>Yes, paste them as plain text in the tool.</> },
43
+ { question: "💡 Can I suggest new features?", answer: <>Email suggestions to <a href="mailto:humanizerx@gmail.com" className="text-indigo-600 hover:underline">humanizerx@gmail.com</a>.</> },
44
+ { question: "🖋 Can I choose formal vs casual style?", answer: <>Yes, select tone when humanizing text.</> },
45
+ { question: "🔢 Is there a limit to text length?", answer: <>Yes, split very large text blocks for performance.</> },
46
+ { question: "📑 Can I humanize text from PDFs?", answer: <>Copy text and paste it into the Text Humanizer tool.</> },
47
+ { question: "🕰 How long does it take?", answer: <>Usually instant for small texts; longer texts may take a few seconds.</> },
48
+ { question: "📋 Does formatting get preserved?", answer: <>Plain text works best; minor formatting may be lost.</> },
49
+ { question: "🛠 Can I adjust readability?", answer: <>Yes, choose simple or advanced wording in settings.</> },
50
+ { question: "🎯 Can I set a target audience?", answer: <>Yes, select audience type for better humanization.</> },
51
+ { question: "🔁 Can I edit the result?", answer: <>Yes, you can further refine the text after humanization.</> },
52
+ { question: "💬 Does it support tone-only changes?", answer: <>Yes, keep content same but modify tone.</> },
53
+ { question: "📌 Can I save humanized text?", answer: <>Copy and save manually; auto-save coming soon.</> },
54
+ { question: "📂 Can I humanize content from Word?", answer: <>Copy text from Word and paste into the tool.</> },
55
+ { question: "🧾 Can I humanize emails?", answer: <>Yes, paste email content directly.</> },
56
+ { question: "✏️ Can it improve grammar?", answer: <>Yes, grammar and style corrections are applied automatically.</> },
57
+ ]
58
+ },
59
+ {
60
+ category: "GPT Checker",
61
+ items: [
62
+ { question: "🕵️‍♂️ How accurate is GPT Checker?", answer: <>High confidence but no detection is perfect.</> },
63
+ { question: "📋 Can I check multiple texts?", answer: <>One text at a time is supported.</> },
64
+ { question: "❌ Does GPT Checker detect AI plagiarism?", answer: <>No, only AI generation.</> },
65
+ { question: "💸 Is GPT Checker free?", answer: <>Basic use is free; advanced features may require subscription.</> },
66
+ { question: "📚 Can I check academic papers?", answer: <>Yes, but check manually for critical use.</> },
67
+ { question: "🔄 Does it detect paraphrased AI content?", answer: <>Partially; confidence scores vary.</> },
68
+ { question: "🖥 Can I use it on desktop?", answer: <>Yes, fully responsive on desktop browsers.</> },
69
+ { question: "📱 Can I use it on mobile?", answer: <>Yes, fully mobile-optimized.</> },
70
+ { question: "🕒 How fast is analysis?", answer: <>Results appear in seconds for standard text lengths.</> },
71
+ { question: "💬 Does it give percentage scores?", answer: <>Yes, each text gets a confidence percentage.</> },
72
+ { question: "🔍 Can I analyze paragraphs?", answer: <>Yes, copy-paste paragraph by paragraph.</> },
73
+ { question: "📖 Can it check books?", answer: <>Split content into chunks for best results.</> },
74
+ { question: "📝 Can it check blog posts?", answer: <>Yes, works perfectly for blogs and articles.</> },
75
+ { question: "📌 Is it safe?", answer: <>Yes, text is not stored permanently.</> },
76
+ { question: "⚙️ Are settings adjustable?", answer: <>Advanced analysis options coming soon.</> },
77
+ { question: "📊 Does it show results visually?", answer: <>Yes, simple charts and confidence indicators are displayed.</> },
78
+ { question: "📁 Can I save results?", answer: <>Currently manual copy; export coming soon.</> },
79
+ { question: "🖋 Can I check tone?", answer: <>Yes, it highlights AI tone detection.</> },
80
+ { question: "🧩 Can I integrate with other tools?", answer: <>API integration coming soon.</> },
81
+ { question: "🔗 Can I share results?", answer: <>Yes, copy and share manually for now.</> },
82
+ ]
83
+ },
84
+ {
85
+ category: "Word Counter",
86
+ items: [
87
+ { question: "🔢 How do I count words and characters?", answer: <>Use <Link to="/word-counter" className="text-indigo-600 hover:underline">Word Counter</Link>.</> },
88
+ { question: "📈 Does it provide readability scores?", answer: <>Yes, Flesch-Kincaid readability included.</> },
89
+ { question: "🗂 Can I analyze large texts?", answer: <>Split very long texts for performance.</> },
90
+ { question: "🔤 Can I count unique words?", answer: <>Yes, unique word counts are shown.</> },
91
+ { question: "📑 Can I count sentences?", answer: <>Yes, total sentences are calculated.</> },
92
+ { question: "⏱ Does it measure reading time?", answer: <>Yes, estimated reading time is displayed.</> },
93
+ { question: "📊 Can I export results?", answer: <>Copy or screenshot results manually.</> },
94
+ { question: "📝 Can I analyze multiple docs?", answer: <>One document at a time for accuracy.</> },
95
+ { question: "📋 Can I check grammar too?", answer: <>No, Word Counter focuses on counts and readability.</> },
96
+ { question: "💻 Is it browser-friendly?", answer: <>Yes, works in all modern browsers.</> },
97
+ { question: "📱 Works on mobile?", answer: <>Fully mobile-optimized.</> },
98
+ { question: "🧾 Can I count paragraphs?", answer: <>Yes, paragraph counts included.</> },
99
+ { question: "🔖 Can I track history?", answer: <>History feature coming soon.</> },
100
+ { question: "🖋 Can it analyze blog posts?", answer: <>Yes, just paste text into Word Counter.</> },
101
+ { question: "🗒 Can it handle code snippets?", answer: <>Yes, counts words and characters in code too.</> },
102
+ { question: "🧮 Does it count symbols?", answer: <>Yes, symbols and punctuation are counted.</> },
103
+ { question: "💼 Can I analyze reports?", answer: <>Yes, any text content can be counted.</> },
104
+ { question: "📌 Can I save reports?", answer: <>Manual copy for now; export coming soon.</> },
105
+ { question: "🔎 Can I search within counted text?", answer: <>Yes, keyword search is supported.</> },
106
+ ]
107
+ },
108
+ {
109
+ category: "Account & Profile",
110
+ items: [
111
+ { question: "👤 Do I need an account?", answer: <>No for basic use. Advanced features may require login.</> },
112
+ { question: "🛠 How do I update profile info?", answer: <>Go to <Link to="/profile" className="text-indigo-600 hover:underline">Profile Page</Link>.</> },
113
+ { question: "📊 Can I access my analytics?", answer: <>Visit <Link to="/analytics" className="text-indigo-600 hover:underline">Analytics</Link>.</> },
114
+ { question: "📝 How do I manage activity feed?", answer: <>Check <Link to="/activity" className="text-indigo-600 hover:underline">Activity Feed</Link>.</> },
115
+ { question: "❌ Can I delete my account?", answer: <>Contact support to delete your account permanently.</> },
116
+ { question: "✏️ How do I change my username?", answer: <>Update it in <Link to="/profile" className="text-indigo-600 hover:underline">Profile Settings</Link>.</> },
117
+ { question: "🔒 Can I update my password?", answer: <>Use settings to reset password securely.</> },
118
+ { question: "🖼 Can I upload a profile picture?", answer: <>Yes, go to Profile Page to upload.</> },
119
+ { question: "📧 Can I change email?", answer: <>Update email in account settings.</> },
120
+ { question: "🌐 Can I use multiple devices?", answer: <>Yes, your account syncs across devices.</> },
121
+ { question: "💼 Can I manage subscriptions?", answer: <>Check <Link to="/settings" className="text-indigo-600 hover:underline">Settings</Link> for subscription details.</> },
122
+ { question: "🔔 Can I enable email notifications?", answer: <>Yes, toggle notifications in profile settings.</> },
123
+ { question: "📂 Can I export my data?", answer: <>Export feature coming soon.</> },
124
+ { question: "🗑 Can I deactivate account temporarily?", answer: <>Contact support to deactivate temporarily.</> },
125
+ { question: "🔗 Can I connect social logins?", answer: <>Coming soon for enhanced login options.</> },
126
+ { question: "🧾 Can I view activity history?", answer: <>Yes, check Activity Feed in profile.</> },
127
+ { question: "⚙️ Can I manage privacy settings?", answer: <>Yes, toggle privacy options in Settings.</> },
128
+ { question: "💬 Can I leave feedback?", answer: <>Email <a href="mailto:humanizerx@gmail.com" className="text-indigo-600 hover:underline">humanizerx@gmail.com</a>.</> },
129
+ { question: "📌 Can I set preferred language?", answer: <>Yes, set language in Settings.</> },
130
+ { question: "🕵️ Can I monitor account activity?", answer: <>Check activity logs in profile for recent logins.</> },
131
+ ]
132
+ },
133
+ {
134
+ category: "Settings & Security",
135
+ items: [
136
+ { question: "🎨 How do I change my theme?", answer: <>Toggle in <Link to="/settings" className="text-indigo-600 hover:underline">Settings</Link>.</> },
137
+ { question: "🔑 Can I reset my password?", answer: <>Reset securely in account settings.</> },
138
+ { question: "🛡 Is my data safe?", answer: <>We do not store text permanently and follow security best practices.</> },
139
+ { question: "🕵️‍♂️ Can I enable 2FA?", answer: <>Yes, 2FA is coming soon in updates.</> },
140
+ { question: "🔔 Can I receive notifications?", answer: <>Yes, enable notifications in settings.</> },
141
+ { question: "🔗 Can I connect social accounts?", answer: <>Coming soon in advanced settings.</> },
142
+ { question: "🖋 Can I change language settings?", answer: <>Yes, choose language in settings.</> },
143
+ { question: "📝 Can I manage connected devices?", answer: <>Yes, view all logged-in devices in Settings.</> },
144
+ { question: "⚠️ Can I monitor security alerts?", answer: <>Yes, alerts will appear in profile notifications.</> },
145
+ { question: "📌 Can I export security logs?", answer: <>Coming soon in advanced security options.</> },
146
+ { question: "🗑 Can I remove old devices?", answer: <>Yes, remove devices manually from Settings.</> },
147
+ { question: "🧩 Can I customize privacy settings?", answer: <>Yes, choose which features share data.</> },
148
+ { question: "💡 Does it support auto-logout?", answer: <>Yes, auto-logout is configurable in Settings.</> },
149
+ { question: "📧 Can I receive security emails?", answer: <>Yes, enable security notifications in profile.</> },
150
+ { question: "🔒 Can I lock account temporarily?", answer: <>Contact support for temporary account locks.</> },
151
+ { question: "🛠 Can I manage API keys?", answer: <>API access coming soon.</> },
152
+ { question: "🗂 Can I backup settings?", answer: <>Backup feature coming soon.</> },
153
+ { question: "📊 Can I view security reports?", answer: <>Reports coming in future updates.</> },
154
+ { question: "⚙️ Can I configure auto-save?", answer: <>Yes, auto-save is coming soon.</> },
155
+ { question: "📌 Can I enforce session timeouts?", answer: <>Yes, configure in Settings.</> },
156
+ ]
157
+ },
158
+ {
159
+ category: "Legal & Policies",
160
+ items: [
161
+ { question: "📃 Where can I read Privacy Policy?", answer: <>Visit <Link to="/privacy-policy" className="text-indigo-600 hover:underline">Privacy Policy</Link>.</> },
162
+ { question: "⚖️ Where are Terms & Conditions?", answer: <>Visit <Link to="/terms" className="text-indigo-600 hover:underline">Terms & Conditions</Link>.</> },
163
+ { question: "📌 Where is the Disclaimer?", answer: <>Visit <Link to="/disclaimer" className="text-indigo-600 hover:underline">Disclaimer Page</Link>.</> },
164
+ { question: "🧾 How is my data used?", answer: <>Read the Privacy Policy for details on data usage.</> },
165
+ { question: "💼 Are there legal obligations?", answer: <>Yes, follow Terms & Conditions when using the platform.</> },
166
+ { question: "📜 Can I request data deletion?", answer: <>Contact support for GDPR-compliant deletion.</> },
167
+ { question: "🛡 Are there safety policies?", answer: <>Yes, we follow strict privacy and security policies.</> },
168
+ { question: "🔖 Are cookies used?", answer: <>Yes, see Privacy Policy for details.</> },
169
+ { question: "📌 Can I download legal documents?", answer: <>Yes, PDF versions available on the respective pages.</> },
170
+ { question: "⚠️ Are there disclaimers?", answer: <>Yes, read the Disclaimer page carefully.</> },
171
+ ]
172
+ },
173
+ {
174
+ category: "Miscellaneous",
175
+ items: [
176
+ { question: "📧 How do I contact support?", answer: <>Email <a href="mailto:humanizerx@gmail.com" className="text-indigo-600 hover:underline">humanizerx@gmail.com</a> or visit <Link to="/contact" className="text-indigo-600 hover:underline">Contact Page</Link>.</> },
177
+ { question: "📱 Can I use Humanizer X on mobile?", answer: <>Yes, fully responsive.</> },
178
+ { question: "💰 Is Humanizer X free?", answer: <>Basic features are free; premium features may require subscription.</> },
179
+ { question: "🧠 Can I suggest new features?", answer: <>Email suggestions to <a href="mailto:humanizerx@gmail.com" className="text-indigo-600 hover:underline">humanizerx@gmail.com</a>.</> },
180
+ { question: "🌟 Are there premium plans?", answer: <>Yes, premium plans include advanced features.</> },
181
+ { question: "📦 Can I export results?", answer: <>Copy text or take screenshots manually.</> },
182
+ { question: "🖥 Does it work offline?", answer: <>Currently online only; offline mode coming soon.</> },
183
+ { question: "🧩 Can I integrate with third-party apps?", answer: <>API integration coming soon.</> },
184
+ { question: "🛠 Can I customize UI?", answer: <>UI customization options coming soon.</> },
185
+ { question: "📌 Can I bookmark FAQs?", answer: <>Bookmark feature coming soon.</> },
186
+ { question: "🔖 Can I save searches?", answer: <>Search history coming in future updates.</> },
187
+ { question: "💬 Can I leave comments?", answer: <>Commenting feature coming soon.</> },
188
+ { question: "📝 Can I submit requests?", answer: <>Submit feature requests via email.</> },
189
+ { question: "🕹 Can I test beta features?", answer: <>Beta features will be available to selected users.</> },
190
+ { question: "📂 Can I organize content?", answer: <>Content organization coming in future updates.</> },
191
+ { question: "📊 Can I view analytics?", answer: <>Analytics dashboard available for premium users.</> },
192
+ { question: "🎯 Can I track usage?", answer: <>Usage tracking coming in future updates.</> },
193
+ { question: "🔗 Can I share content?", answer: <>Share manually via copy or email.</> },
194
+ { question: "🌐 Does it support multiple browsers?", answer: <>Yes, works in all modern browsers.</> },
195
+ { question: "📌 Can I suggest improvements?", answer: <>Yes, email suggestions to support.</> },
196
+ { question: "🧾 Can I get tutorials?", answer: <>Tutorials coming soon on the Help Center.</> },
197
+ ]
198
+ },
199
+ ];
200
+
201
+
202
+ const filteredFaqs = useMemo(() => {
203
+ if (!searchTerm.trim()) return faqs;
204
+ return faqs.map(group => ({
205
+ ...group,
206
+ items: group.items.filter(f =>
207
+ f.question.toLowerCase().includes(searchTerm.toLowerCase()) ||
208
+ (typeof f.answer === "string" && f.answer.toLowerCase().includes(searchTerm.toLowerCase()))
209
+ )
210
+ })).filter(group => group.items.length > 0);
211
+ }, [searchTerm, faqs]);
212
+
213
+ const handleSearch = () => setSearchTerm(inputValue);
214
+ const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => { if (e.key === "Enter") handleSearch(); };
215
+
216
+ return (
217
+ <div className={`min-h-screen flex flex-col transition-colors duration-300 ${bgPage}`}>
218
+ <Helmet>
219
+ <title>Help Center – Get Support & Resources | HumanizerX</title>
220
+ <meta
221
+ name="description"
222
+ content="Find answers, FAQs, and support for Humanizer X tools including Text Humanizer, GPT Checker, and Word Counter."
223
+ />
224
+ <link
225
+ rel="canonical"
226
+ href="https://www.humanizerx.pro/help-center"
227
+ />
228
+ </Helmet>
229
+ <Header />
230
+
231
+ {/* Hero */}
232
+ <section className="bg-gradient-to-r from-indigo-900 to-slate-900 text-white py-20 text-center">
233
+ <div className="container mx-auto px-4">
234
+ <img
235
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
236
+ alt="Humanizer X Help Center - Support and FAQ Logo"
237
+ title="Humanizer X Help Center - Find answers to your questions"
238
+ className="mx-auto w-40 h-30 object-contain mb-6"
239
+ />
240
+ <h1 className="text-5xl md:text-6xl font-bold mb-4">
241
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
242
+ Help
243
+ </span>{" "}
244
+ Center
245
+ </h1>
246
+ <p className="text-lg md:text-xl max-w-3xl mx-auto text-blue-100">
247
+ Find guides, FAQs, and resources for all Humanizer X tools—from humanizing AI text to checking GPT content and counting words.
248
+ </p>
249
+ </div>
250
+ </section>
251
+
252
+ {/* Search Bar below hero */}
253
+ <div className="container mx-auto px-4 -mt-8 mb-12 relative">
254
+ <div
255
+ className={`flex items-center max-w-xl mx-auto rounded-lg shadow-lg border overflow-hidden
256
+ ${theme === 'dark'
257
+ ? 'bg-gray-800 border-gray-700'
258
+ : 'bg-white border-gray-300'
259
+ }`}
260
+ >
261
+ <input
262
+ type="text"
263
+ value={inputValue}
264
+ onChange={e => setInputValue(e.target.value)}
265
+ onKeyPress={handleKeyPress}
266
+ placeholder={inputValue ? '' : 'Search FAQs...'}
267
+ className="flex-1 p-4 bg-transparent focus:outline-none transition-colors duration-200"
268
+ style={{
269
+ color: theme === 'dark' ? '#fff' : '#1f2937',
270
+ caretColor: theme === 'dark' ? '#fff' : '#1f2937',
271
+ }}
272
+ />
273
+ <button
274
+ onClick={handleSearch}
275
+ className={`px-4 transition-colors duration-200 ${
276
+ theme === 'dark'
277
+ ? 'text-indigo-400 hover:text-indigo-200'
278
+ : 'text-indigo-600 hover:text-indigo-800'
279
+ }`}
280
+ >
281
+ <FiSearch size={24} />
282
+ </button>
283
+ </div>
284
+ </div>
285
+
286
+
287
+ <main className="container mx-auto px-4 py-12 flex-1">
288
+ {/* Tools Section */}
289
+ <section className="mb-12">
290
+ <h2 className={`text-3xl font-bold mb-6 ${textPrimary}`}>Our Tools</h2>
291
+ <div className="grid md:grid-cols-3 gap-6">
292
+ {tools.map(tool => (
293
+ <div key={tool.name} className={`${bgCard} p-6 rounded-xl shadow-lg transition hover:shadow-xl`}>
294
+ <h3 className={`text-xl font-semibold mb-2 ${textPrimary}`}>
295
+ <Link to={tool.path} className="hover:text-indigo-500">{tool.name}</Link>
296
+ </h3>
297
+ <p className={`text-sm ${textSecondary}`}>{tool.description}</p>
298
+ </div>
299
+ ))}
300
+ </div>
301
+ </section>
302
+
303
+ {/* FAQ Section */}
304
+ <section className="mb-12">
305
+ <h2 className={`text-3xl font-bold mb-6 ${textPrimary}`}>Frequently Asked Questions</h2>
306
+ {filteredFaqs.length === 0 ? (
307
+ <p className={`${textSecondary} text-center`}>No results found for "{searchTerm}" 😔</p>
308
+ ) : (
309
+ filteredFaqs.map((group, idx) => (
310
+ <div key={idx} className="mb-8">
311
+ <h3 className={`text-2xl font-semibold mb-4 ${textPrimary}`}>{group.category}</h3>
312
+ <div className="space-y-4">
313
+ {group.items.map((faq, index) => {
314
+ const globalIndex = idx * 1000 + index;
315
+ return (
316
+ <div key={globalIndex} className={`${bgCard} rounded-xl shadow-lg`}>
317
+ <button
318
+ onClick={() => toggleIndex(globalIndex)}
319
+ className={`w-full text-left p-6 flex justify-between items-center ${textPrimary} font-medium`}
320
+ >
321
+ {faq.question}
322
+ <span className="ml-2 text-indigo-500">{openIndex === globalIndex ? "−" : "+"}</span>
323
+ </button>
324
+ {openIndex === globalIndex && (
325
+ <div className={`p-6 pt-0 text-sm ${textSecondary}`}>
326
+ {faq.answer}
327
+ </div>
328
+ )}
329
+ </div>
330
+ );
331
+ })}
332
+ </div>
333
+ </div>
334
+ ))
335
+ )}
336
+ </section>
337
+
338
+ {/* Contact / Support */}
339
+ <section className="mt-12 text-center">
340
+ <h2 className={`text-3xl font-bold mb-4 ${textPrimary}`}>Need More Help?</h2>
341
+ <p className={`mb-6 ${textSecondary}`}>
342
+ If your question isn’t answered here, feel free to <Link to="/contact" className="text-indigo-600 hover:underline">contact our support team</Link>.
343
+ </p>
344
+ <Link
345
+ to="/contact"
346
+ className="inline-block px-6 py-3 bg-gradient-to-r from-indigo-900 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 transition-all duration-200"
347
+ >
348
+ Contact Support
349
+ </Link>
350
+ </section>
351
+ </main>
352
+
353
+ <Footer />
354
+ </div>
355
+ );
356
+ };
357
+
358
+ export default HelpCenterPage;
src/pages/HomePage.tsx ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/HomePage.tsx
2
+ import React, { useState, useContext } from "react";
3
+ import { Link } from "react-router-dom";
4
+ import { Sparkles, Shield, FileText, Zap, ChevronRight } from "lucide-react";
5
+ import Header from "../components/Header";
6
+ import Footer from "../components/Footer";
7
+ import HumanizerInterface from "../components/HumanizerInterface";
8
+ import { ThemeContext } from "../contexts/ThemeContext";
9
+ import { Helmet } from "react-helmet-async";
10
+
11
+ const HomePage: React.FC = () => {
12
+ const [currentMode, setCurrentMode] = useState("normal");
13
+ const { theme } = useContext(ThemeContext);
14
+
15
+ // Helper classes
16
+ const bgClass = theme === "dark" ? "bg-gray-900" : "bg-gray-50";
17
+ const textPrimary = theme === "dark" ? "text-white" : "text-gray-900";
18
+ const textSecondary = theme === "dark" ? "text-gray-300" : "text-gray-600";
19
+
20
+ return (
21
+ <div className={`min-h-screen transition-colors duration-300 ${bgClass} ${textPrimary}`}>
22
+ {/* ✅ Canonical + SEO tags */}
23
+ <Helmet>
24
+ <title>HumanizerX – Natural, Human-Like AI Text Made Easy</title>
25
+ <meta
26
+ name="description"
27
+ content="HumanizerX transforms AI-generated content into natural, human-like text. Detect GPT content, enhance readability, and make your writing authentic and engaging."
28
+ />
29
+ <link rel="canonical" href="https://www.humanizerx.pro/" />
30
+ </Helmet>
31
+ <Header />
32
+
33
+ {/* Hero Section */}
34
+ <section className="bg-gradient-to-r from-indigo-900 to-slate-900 text-white py-20">
35
+ <div className="container mx-auto px-4 text-center">
36
+ <img
37
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
38
+ alt="Humanizer X - AI Text Humanization Tool Logo"
39
+ title="Humanizer X - Transform AI text into natural, human-like content"
40
+ className="mx-auto w-40 h-30 object-contain mb-6"
41
+ />
42
+ <h1 className="text-5xl md:text-6xl font-bold leading-tight">
43
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
44
+ Humanizer
45
+ </span>{" "}
46
+ <span className="text-white">X</span>
47
+ </h1>
48
+
49
+ <p className="text-xl md:text-2xl mt-6 mb-8 text-blue-100">
50
+ Transform AI-generated content into natural, human-like text with advanced processing modes
51
+ </p>
52
+
53
+ <div className="flex flex-col sm:flex-row gap-4 justify-center items-center min-h-[60px]">
54
+ <div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-3 w-full sm:w-auto flex items-center justify-center min-w-[200px]">
55
+ <span className="text-lg font-semibold">Current Mode: </span>
56
+ <span className="text-blue-200 capitalize ml-1" aria-live="polite">{currentMode}</span>
57
+ </div>
58
+
59
+ <Link
60
+ to="/gpt-checker"
61
+ className="inline-flex items-center gap-2 bg-white text-purple-600 px-6 py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors"
62
+ >
63
+ <Shield size={20} />
64
+ GPT Checker
65
+ <ChevronRight size={16} />
66
+ </Link>
67
+ </div>
68
+ </div>
69
+ </section>
70
+
71
+ {/* Main Humanizer Interface */}
72
+ <section className="py-12">
73
+ {/* Humanization mode cards */}
74
+ </section>
75
+
76
+ <div className="container mx-auto px-4">
77
+ <HumanizerInterface onModeChange={setCurrentMode} />
78
+ </div>
79
+ {/* Powerful Features Section */}
80
+ <section className="py-16">
81
+ <div className="container mx-auto px-4">
82
+ {/* Card Container */}
83
+ <div className={`${theme === 'dark' ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'} rounded-xl shadow-lg border p-8`}>
84
+
85
+ {/* Section Title */}
86
+ <div className="text-center mb-12">
87
+ <h2 className={`text-3xl font-bold mb-4 ${textPrimary}`}>Humanizer X Features</h2>
88
+ <p className={`text-lg max-w-2xl mx-auto ${textSecondary}`}>
89
+ Advanced text processing capabilities designed for content creators, students, and professionals
90
+ </p>
91
+ </div>
92
+
93
+ {/* Feature Boxes */}
94
+ <div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
95
+ {/* Box 1 */}
96
+ <div className={`p-6 rounded-xl shadow-sm ${theme === 'dark' ? 'bg-gray-700' : 'bg-white'}`}>
97
+ <div className={`w-12 h-12 rounded-lg flex items-center justify-center mb-4 ${theme === 'dark' ? 'bg-blue-900' : 'bg-blue-100'}`}>
98
+ <Sparkles className={`${theme === 'dark' ? 'text-blue-400' : 'text-blue-600'}`} size={24} />
99
+ </div>
100
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>Smart Synonyms</h3>
101
+ <p className={`text-sm ${textSecondary}`}>
102
+ Intelligent synonym replacement that preserves context and meaning
103
+ </p>
104
+ </div>
105
+
106
+ {/* Box 2 */}
107
+ <div className={`p-6 rounded-xl shadow-sm ${theme === 'dark' ? 'bg-gray-700' : 'bg-white'}`}>
108
+ <div className={`w-12 h-12 rounded-lg flex items-center justify-center mb-4 ${theme === 'dark' ? 'bg-purple-900' : 'bg-purple-100'}`}>
109
+ <FileText className={`${theme === 'dark' ? 'text-purple-400' : 'text-purple-600'}`} size={24} />
110
+ </div>
111
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>Sentence Reconstruction</h3>
112
+ <p className={`text-sm ${textSecondary}`}>
113
+ Advanced sentence restructuring while maintaining original meaning
114
+ </p>
115
+ </div>
116
+
117
+ {/* Box 3 */}
118
+ <div className={`p-6 rounded-xl shadow-sm ${theme === 'dark' ? 'bg-gray-700' : 'bg-white'}`}>
119
+ <div className={`w-12 h-12 rounded-lg flex items-center justify-center mb-4 ${theme === 'dark' ? 'bg-green-900' : 'bg-green-100'}`}>
120
+ <Shield className={`${theme === 'dark' ? 'text-green-400' : 'text-green-600'}`} size={24} />
121
+ </div>
122
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>Content Preservation</h3>
123
+ <p className={`text-sm ${textSecondary}`}>
124
+ Protects headings, titles, and keywords from modification
125
+ </p>
126
+ </div>
127
+
128
+ {/* Box 4 */}
129
+ <div className={`p-6 rounded-xl shadow-sm ${theme === 'dark' ? 'bg-gray-700' : 'bg-white'}`}>
130
+ <div className={`w-12 h-12 rounded-lg flex items-center justify-center mb-4 ${theme === 'dark' ? 'bg-orange-900' : 'bg-orange-100'}`}>
131
+ <Zap className={`${theme === 'dark' ? 'text-orange-400' : 'text-orange-600'}`} size={24} />
132
+ </div>
133
+ <h3 className={`text-lg font-semibold mb-2 ${textPrimary}`}>SEO Optimized</h3>
134
+ <p className={`text-sm ${textSecondary}`}>
135
+ Output optimized for search engines and human readability
136
+ </p>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </section>
142
+ {/* --- Article Section --- */}
143
+ <section className="py-16 mb-16">
144
+ <div className="container mx-auto px-4">
145
+ <div
146
+ className={`${
147
+ theme === "dark"
148
+ ? "bg-gray-800 border-gray-700"
149
+ : "bg-white border-gray-300"
150
+ } rounded-xl shadow-lg border p-10 space-y-8 transform scale-100 transition-transform duration-300`}
151
+ >
152
+ {/* Main Title */}
153
+ <h2 className="text-5xl md:text-5xl font-bold text-gray-900 dark:text-gray-100">
154
+ Make AI Sound Human – The Power of Humanizer X
155
+ </h2>
156
+
157
+ {/* Intro Paragraphs */}
158
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
159
+ In today’s world, content is a strong tool for every writer, student,
160
+ and business. People employ AI tools to create text faster. Yet, AI text
161
+ often looks robotic and not natural. Readers want words that feel human
162
+ — easy to follow. This is where Humanizer X comes in.
163
+ </p>
164
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
165
+ We built Humanizer X to produce AI content that sounds real and
166
+ friendly. Every piece of text should connect with people. Our tool is
167
+ simple, smart, and free to leverage. With it, you can turn AI text into
168
+ human-like writing in seconds.
169
+ </p>
170
+
171
+ {/* Subheadings & Content */}
172
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
173
+ What is Humanizer X?
174
+ </h3>
175
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
176
+ Humanizer X is an AI Text Converter that transforms robotic AI writing
177
+ into clear, human-aesthetic content. Many users create drafts with AI
178
+ models. Often, the text feels stiff, repetitive, or flagged by a GPT
179
+ Checker.
180
+ </p>
181
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
182
+ We solve this problem. Our AI Humanizer makes the content flow like
183
+ natural speech. It removes the robotic tone and adds clarity. With our
184
+ tool, users can humanize AI text and make it more engaging.
185
+ </p>
186
+
187
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
188
+ Why Humanizer X Matters Today?
189
+ </h3>
190
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
191
+ Content plays a substantial role in communication. Businesses need
192
+ blogs, social posts, and emails to connect with people. Students need
193
+ essays that sound original. Writers want content that feels personal.
194
+ </p>
195
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
196
+ AI alone can't do this. Robots often repeat phrases and miss emotions.
197
+ That's why people need Humanizer X. We provide a smart solution to
198
+ humanize AI text quickly. With Humanizer X, you get content that passes
199
+ GPT Checker tests, connects with readers, and flows naturally.
200
+ </p>
201
+
202
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
203
+ How Humanizer X Works?
204
+ </h3>
205
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
206
+ We designed our tool to be simple for everyone. No technical skill is
207
+ required:
208
+ </p>
209
+ <ol className="text-lg md:text-xl text-gray-800 dark:text-gray-300 list-decimal list-inside space-y-2">
210
+ <li>
211
+ Open our website:{" "}
212
+ <a
213
+ href="https://humanizerx.pro/"
214
+ className="text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200 underline"
215
+ target="_blank"
216
+ rel="noopener noreferrer"
217
+ >
218
+ https://humanizerx.pro/
219
+ </a>
220
+ </li>
221
+ <li>Paste your AI-generated text into the box.</li>
222
+ <li>Click the Humanize button.</li>
223
+ <li>
224
+ Get your text in human style within seconds. Copy or download the
225
+ result.
226
+ </li>
227
+ </ol>
228
+
229
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
230
+ Humanizer X vs Other Tools:
231
+ </h3>
232
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
233
+ Many tools claim to humanize AI text but often add complex words or
234
+ change meaning. Humanizer X doesn’t. We keep your ideas intact and only
235
+ change the tone to make it natural. Our focus is on clarity, tone, and
236
+ flow.
237
+ </p>
238
+
239
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
240
+ Benefits of Using Humanizer X:
241
+ </h3>
242
+ <ul className="text-lg md:text-xl text-gray-800 dark:text-gray-300 list-disc list-inside space-y-2">
243
+ <li>Human-like text: Words flow like natural speech.</li>
244
+ <li>Easy to read: Sentences are short and simple.</li>
245
+ <li>Passes GPT Checker: Content looks original and natural.</li>
246
+ <li>Fast process: Convert text in seconds.</li>
247
+ <li>Global reach: Works for people everywhere.</li>
248
+ </ul>
249
+
250
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
251
+ Who Can Implement Humanizer X?
252
+ </h3>
253
+ <ul className="text-lg md:text-xl text-gray-800 dark:text-gray-300 list-disc list-inside space-y-2">
254
+ <li>Students: Make essays sound original and clear.</li>
255
+ <li>Writers: Transform drafts into polished articles.</li>
256
+ <li>Businesses: Create blogs and posts that connect with readers.</li>
257
+ <li>Freelancers: Deliver content that feels human and natural.</li>
258
+ <li>Marketers: Humanize AI text for campaigns and ads.</li>
259
+ </ul>
260
+
261
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
262
+ Humanizer X and SEO:
263
+ </h3>
264
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
265
+ SEO is vital for online success. Google rewards content that's clear,
266
+ natural, and user-focused. Robotic AI text often fails these rules.
267
+ Humanizer X shapes sentences to match human readability and semantic
268
+ SEO, making your content more visible online.
269
+ </p>
270
+
271
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
272
+ Choose Humanizer X for Humanizing AI Text:
273
+ </h3>
274
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
275
+ We focus on readers, not just algorithms. We keep content simple and
276
+ global. We ensure text passes GPT Checker tools and supports growth for
277
+ students, writers, and businesses.
278
+ </p>
279
+
280
+ <h3 className="text-3xl md:text-4xl font-bold text-gray-900 dark:text-gray-100">
281
+ Final Thoughts:
282
+ </h3>
283
+ <p className="text-lg md:text-xl text-gray-800 dark:text-gray-300">
284
+ Content should be human, not robotic. That is what we believe at
285
+ Humanizer X. With our AI Text Converter, users can humanize AI text in
286
+ seconds. We serve people worldwide with a free, simple, and smart tool.
287
+ If you require content that works for readers and search engines, try
288
+ Humanizer X today:{" "}
289
+ <a
290
+ href="https://humanizerx.pro/"
291
+ className="text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200 underline"
292
+ target="_blank"
293
+ rel="noopener noreferrer"
294
+ >
295
+ https://humanizerx.pro/
296
+ </a>
297
+ </p>
298
+ </div>
299
+ </div>
300
+ </section>
301
+
302
+
303
+ {/* CTA Section */}
304
+ <section className="py-16 bg-gradient-to-r from-indigo-900 to-slate-900 text-white">
305
+ <div className="container mx-auto px-4 text-center">
306
+ <h2 className="text-3xl font-bold mb-4">Ready to Check Your Content?</h2>
307
+ <p className="text-xl mb-8 text-blue-100">
308
+ Use our advanced GPT checker to detect AI-generated content and plagiarism
309
+ </p>
310
+ <Link
311
+ to="/gpt-checker"
312
+ className="inline-flex items-center gap-2 bg-white text-indigo-600 px-8 py-4 rounded-lg font-semibold text-lg hover:bg-gray-100 transition-colors"
313
+ >
314
+ <Shield size={24} />
315
+ Try GPT Checker
316
+ <ChevronRight size={20} />
317
+ </Link>
318
+ </div>
319
+ </section>
320
+
321
+ <Footer />
322
+ </div>
323
+ );
324
+ };
325
+
326
+ export default HomePage;
327
+
src/pages/LoginPage.tsx ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/LoginPage.tsx
2
+ import React, { useState, useContext, useEffect } from 'react';
3
+ import { Link, useNavigate } from 'react-router-dom';
4
+ import { Mail, Lock, Eye, EyeOff, ArrowLeft } from 'lucide-react';
5
+ import {
6
+ signInWithEmailAndPassword,
7
+ sendEmailVerification,
8
+ sendPasswordResetEmail,
9
+ signInWithPopup,
10
+ signOut as firebaseSignOut,
11
+ } from 'firebase/auth';
12
+ import { doc, getDoc, updateDoc, setDoc, collection, getDocs } from 'firebase/firestore';
13
+ import Header from '../components/Header';
14
+ import Footer from '../components/Footer';
15
+ import { auth, provider, db } from '../firebase';
16
+ import { ThemeContext } from '../contexts/ThemeContext';
17
+
18
+ const LoginPage: React.FC = () => {
19
+ const navigate = useNavigate();
20
+ const { theme } = useContext(ThemeContext);
21
+ const [formData, setFormData] = useState({ email: '', password: '' });
22
+ const [showPassword, setShowPassword] = useState(false);
23
+ const [isLoading, setIsLoading] = useState(false);
24
+ const [message, setMessage] = useState('');
25
+ const [loginTime, setLoginTime] = useState<Date | null>(null);
26
+
27
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
28
+ const { name, value } = e.target;
29
+ setFormData(prev => ({ ...prev, [name]: value }));
30
+ };
31
+
32
+ // --- Ensure totalDuration is initialized for all users ---
33
+ const initializeUserStats = async (userId: string) => {
34
+ const userRef = doc(db, 'users', userId);
35
+ const snap = await getDoc(userRef);
36
+
37
+ if (!snap.exists()) {
38
+ await setDoc(userRef, {
39
+ textsHumanized: 0,
40
+ wordsProcessed: 0,
41
+ gptAverage: 0,
42
+ totalSessions: 0,
43
+ totalDuration: 0, // ✅ initialize here
44
+ averageSessionTime: 0,
45
+ });
46
+ } else {
47
+ const data = snap.data();
48
+ if (data.totalDuration === undefined) {
49
+ await updateDoc(userRef, {
50
+ totalDuration: data.averageSessionTime * (data.totalSessions ?? 1),
51
+ });
52
+ }
53
+ }
54
+ };
55
+
56
+ // --- Increment total sessions ---
57
+ const incrementSession = async (userId: string) => {
58
+ const userRef = doc(db, 'users', userId);
59
+ const snap = await getDoc(userRef);
60
+
61
+ if (snap.exists()) {
62
+ const data = snap.data();
63
+ await updateDoc(userRef, {
64
+ totalSessions: (data.totalSessions ?? 0) + 1,
65
+ });
66
+ }
67
+ };
68
+
69
+ // --- Update session duration on logout ---
70
+ const updateSessionDuration = async (userId: string) => {
71
+ if (!loginTime) return;
72
+ const now = new Date();
73
+ const duration = (now.getTime() - loginTime.getTime()) / 60000; // in minutes
74
+ const userRef = doc(db, 'users', userId);
75
+ const snap = await getDoc(userRef);
76
+ if (snap.exists()) {
77
+ const data = snap.data();
78
+ const totalDur = (data.totalDuration ?? 0) + duration;
79
+ const totalSess = data.totalSessions ?? 1;
80
+ await updateDoc(userRef, {
81
+ totalDuration: totalDur,
82
+ averageSessionTime: totalDur / totalSess,
83
+ });
84
+ }
85
+ };
86
+
87
+ // --- Email/Password login ---
88
+ const handleSubmit = async (e: React.FormEvent) => {
89
+ e.preventDefault();
90
+ setIsLoading(true);
91
+ setMessage('');
92
+ try {
93
+ const userCredential = await signInWithEmailAndPassword(auth, formData.email, formData.password);
94
+ const user = userCredential.user;
95
+ if (user && !user.emailVerified) {
96
+ await sendEmailVerification(user);
97
+ setMessage('Verification email sent! Check your inbox.');
98
+ } else if (user) {
99
+ await initializeUserStats(user.uid);
100
+ setLoginTime(new Date());
101
+ await incrementSession(user.uid);
102
+ navigate('/welcome');
103
+ }
104
+ } catch (error: any) {
105
+ setMessage(error.message);
106
+ } finally {
107
+ setIsLoading(false);
108
+ }
109
+ };
110
+
111
+ // --- Google login ---
112
+ const handleGoogleLogin = async () => {
113
+ setIsLoading(true);
114
+ setMessage('');
115
+ try {
116
+ const result = await signInWithPopup(auth, provider);
117
+ const user = result.user;
118
+ if (user && !user.emailVerified) {
119
+ await sendEmailVerification(user);
120
+ setMessage('Verification email sent! Verify before continuing.');
121
+ } else if (user) {
122
+ await initializeUserStats(user.uid);
123
+ setLoginTime(new Date());
124
+ await incrementSession(user.uid);
125
+ navigate('/welcome');
126
+ }
127
+ } catch (error: any) {
128
+ setMessage(error.message);
129
+ } finally {
130
+ setIsLoading(false);
131
+ }
132
+ };
133
+
134
+ // --- Forgot password ---
135
+ const handleForgotPassword = async () => {
136
+ if (!formData.email) {
137
+ setMessage('Please enter your email first.');
138
+ return;
139
+ }
140
+ try {
141
+ await sendPasswordResetEmail(auth, formData.email);
142
+ setMessage('Password reset email sent! Check inbox.');
143
+ } catch (error: any) {
144
+ setMessage(error.message);
145
+ }
146
+ };
147
+
148
+ // --- Sign out ---
149
+ const handleSignOut = async () => {
150
+ if (auth.currentUser) {
151
+ await updateSessionDuration(auth.currentUser.uid);
152
+ await firebaseSignOut(auth);
153
+ setLoginTime(null);
154
+ navigate('/login');
155
+ }
156
+ };
157
+
158
+ // --- Persist login time on mount ---
159
+ useEffect(() => {
160
+ const unsubscribe = auth.onAuthStateChanged(user => {
161
+ if (user) setLoginTime(new Date());
162
+ });
163
+ return () => unsubscribe();
164
+ }, []);
165
+
166
+ return (
167
+ <div className={`${theme} min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors duration-300`}>
168
+ <Header />
169
+ <main className="container mx-auto px-4 py-8 flex items-center justify-center min-h-[calc(100vh-200px)]">
170
+ <div className="w-full max-w-md">
171
+ <div className="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-8 relative">
172
+ <button onClick={() => navigate(-1)} className="absolute top-4 left-4 text-gray-500 hover:text-gray-900 dark:hover:text-white">
173
+ <ArrowLeft size={20} />
174
+ </button>
175
+
176
+ <div className="text-center mb-8">
177
+ <img src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/" alt="logo" className="mx-auto w-20 h-20 rounded-full" />
178
+ <h1 className="text-2xl font-bold text-gray-900 dark:text-white mt-4">Welcome Back</h1>
179
+ <p className="text-gray-600 dark:text-gray-300">Login to continue</p>
180
+ </div>
181
+
182
+ <form onSubmit={handleSubmit} className="space-y-4">
183
+ <div className="relative">
184
+ <Mail className="absolute top-3 left-3 text-gray-400" />
185
+ <input
186
+ type="email"
187
+ name="email"
188
+ placeholder="Email"
189
+ value={formData.email}
190
+ onChange={handleInputChange}
191
+ className="w-full pl-10 pr-3 py-2 rounded-lg border border-gray-300 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
192
+ required
193
+ />
194
+ </div>
195
+
196
+ <div className="relative">
197
+ <Lock className="absolute top-3 left-3 text-gray-400" />
198
+ <input
199
+ type={showPassword ? 'text' : 'password'}
200
+ name="password"
201
+ placeholder="Password"
202
+ value={formData.password}
203
+ onChange={handleInputChange}
204
+ className="w-full pl-10 pr-10 py-2 rounded-lg border border-gray-300 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500"
205
+ required
206
+ />
207
+ <button
208
+ type="button"
209
+ onClick={() => setShowPassword(!showPassword)}
210
+ className="absolute top-2.5 right-3 text-gray-400 hover:text-gray-700 dark:hover:text-white"
211
+ >
212
+ {showPassword ? <EyeOff /> : <Eye />}
213
+ </button>
214
+ </div>
215
+
216
+ {message && <p className="text-red-500 text-sm">{message}</p>}
217
+
218
+ <button
219
+ type="submit"
220
+ disabled={isLoading}
221
+ className="w-full bg-indigo-600 hover:bg-indigo-700 text-white py-2 rounded-lg font-semibold transition-colors"
222
+ >
223
+ {isLoading ? 'Loading...' : 'Login'}
224
+ </button>
225
+
226
+ <button
227
+ type="button"
228
+ onClick={handleGoogleLogin}
229
+ disabled={isLoading}
230
+ className="w-full bg-red-600 hover:bg-red-700 text-white py-2 rounded-lg font-semibold transition-colors mt-2"
231
+ >
232
+ {isLoading ? 'Loading...' : 'Login with Google'}
233
+ </button>
234
+
235
+ <button
236
+ type="button"
237
+ onClick={handleForgotPassword}
238
+ className="w-full text-indigo-600 hover:underline mt-2"
239
+ >
240
+ Forgot password?
241
+ </button>
242
+ </form>
243
+ </div>
244
+ </div>
245
+ </main>
246
+ <Footer />
247
+ </div>
248
+ );
249
+ };
250
+
251
+ export default LoginPage;
src/pages/PrivacyPolicyPage.tsx ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/PrivacyPolicyPage.tsx
2
+ import React, { useContext } from "react";
3
+ import Header from "../components/Header";
4
+ import Footer from "../components/Footer";
5
+ import { ThemeContext } from "../contexts/ThemeContext";
6
+ import { Helmet } from "react-helmet-async";
7
+
8
+ const PrivacyPolicyPage: React.FC = () => {
9
+ const { theme } = useContext(ThemeContext);
10
+
11
+ const textPrimary = theme === "dark" ? "text-white" : "text-gray-900";
12
+ const textSecondary = theme === "dark" ? "text-gray-300" : "text-gray-600";
13
+
14
+ return (
15
+ <div
16
+ className={`min-h-screen transition-colors duration-300 ${
17
+ theme === "dark" ? "bg-gray-900" : "bg-gray-50"
18
+ }`}
19
+ >
20
+ {/* ✅ SEO + Canonical */}
21
+ <Helmet>
22
+ <title>Privacy Policy – How HumanizerX Protects Your Data</title>
23
+ <meta
24
+ name="description"
25
+ content="Read Humanizer X's Privacy Policy to learn how we collect, use, and protect your personal data in compliance with GDPR, CCPA, and global privacy standards."
26
+ />
27
+ <link
28
+ rel="canonical"
29
+ href="https://www.humanizerx.pro/privacy-policy"
30
+ />
31
+ </Helmet>
32
+ <Header />
33
+
34
+ <section className="py-16 mb-16">
35
+ <div className="container mx-auto px-4 max-w-6xl">
36
+ <div className={`${theme === "dark" ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"} rounded-xl shadow-lg border p-10 space-y-8`}>
37
+
38
+ {/* Logo and Header */}
39
+ <div className="text-center">
40
+ <img
41
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
42
+ alt="Humanizer X Privacy Policy - Data Protection Information Logo"
43
+ title="Humanizer X Privacy Policy - Learn how we protect your data"
44
+ className="mx-auto w-40 h-30 object-contain mb-6"
45
+ />
46
+ <h1 className={`text-4xl md:text-5xl font-bold mb-4 ${textPrimary}`}>Privacy Policy</h1>
47
+ </div>
48
+
49
+ {/* Privacy Policy Content */}
50
+ <div className="space-y-6">
51
+ <h2 className={`text-4xl md:text-3xl font-bold ${textPrimary}`}>Make AI Sound Human – The Power of Humanizer X</h2>
52
+ <p className={`${textSecondary} text-lg md:text-xl`}>
53
+ At{" "}
54
+ <a
55
+ href="https://www.humanizerx.pro/"
56
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
57
+ target="_blank"
58
+ rel="noopener noreferrer"
59
+ >
60
+ Humanizer X
61
+ </a>
62
+ , one of our main priorities is the privacy of our visitors. This Privacy Policy document contains types of information that are collected and recorded by Humanizer X and how we utilize it.
63
+ </p>
64
+
65
+ <p className={`${textSecondary} text-lg md:text-xl`}>
66
+ If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact us.
67
+ </p>
68
+
69
+ <p className={`${textSecondary} text-lg md:text-xl`}>
70
+ This Privacy Policy applies only to our online activities. It is valid for visitors to our website regarding the information they shared and/or collected in Humanizer X. This policy doesn't apply to any information collected offline or via channels other than this website.
71
+ </p>
72
+
73
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Consent:</h2>
74
+ <p className={`${textSecondary} text-lg md:text-xl`}>
75
+ By using our website, you hereby consent to our Privacy Policy and agree to its terms.
76
+ </p>
77
+
78
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Information We Collect:</h2>
79
+ <p className={`${textSecondary} text-lg md:text-xl`}>
80
+ The personal information that you're asked to provide, and the reasons why you're asked to provide it, will be made clear to you at the point we ask you to provide your personal information.
81
+ </p>
82
+ <p className={`${textSecondary} text-lg md:text-xl`}>
83
+ If you{" "}
84
+ <a
85
+ href="https://humanizerx.pro/contact"
86
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
87
+ target="_blank"
88
+ rel="noopener noreferrer"
89
+ >
90
+ contact us
91
+ </a>{" "}
92
+ directly, we may receive additional information about you, such as your name,
93
+ email address, phone number, the contents of the message and/or attachments
94
+ you may send us, and any other information you may choose to provide.
95
+ </p>
96
+
97
+ <p className={`${textSecondary} text-lg md:text-xl`}>
98
+ When you register for an Account, we may ask for your contact information, including items such as your name, email address, and telephone number.
99
+ </p>
100
+
101
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>How We Use Your Information?</h2>
102
+ <p className={`${textSecondary} text-lg md:text-xl`}>
103
+ We use the information we collect in various ways, including to:
104
+ </p>
105
+ <ul className={`${textSecondary} text-lg md:text-xl list-disc list-inside space-y-2`}>
106
+ <li>Provide, operate, and maintain our website</li>
107
+ <li>Improve, personalize, and expand our website</li>
108
+ <li>Understand and analyze how you use our website</li>
109
+ <li>Develop new products, services, features, and functionality</li>
110
+ <li>Communicate with you directly or via partners, including marketing updates</li>
111
+ <li>Send you emails</li>
112
+ <li>Find and prevent fraud</li>
113
+ </ul>
114
+
115
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Log Files:</h2>
116
+ <p className={`${textSecondary} text-lg md:text-xl`}>
117
+ <a
118
+ href="https://www.humanizerx.pro"
119
+ target="_blank"
120
+ rel="noopener noreferrer"
121
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
122
+ >
123
+ Humanizer X
124
+ </a>{" "}
125
+ follows a standard procedure of using log files. These files log visitors when they visit websites. Information collected includes IP addresses, browser type, ISP, date/time, referring/exit pages, and clicks. This is used for analyzing trends and improving the website.
126
+ </p>
127
+
128
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Cookies and Web Beacons:</h2>
129
+ <p className={`${textSecondary} text-lg md:text-xl`}>
130
+ Humanizer X uses cookies to store information, including visitor preferences and pages accessed. This helps optimize user experience.
131
+ </p>
132
+
133
+ <p className={`${textSecondary} text-lg md:text-xl`}>
134
+ Google is a third-party vendor and uses DART cookies to serve ads. Users may opt-out via{" "}
135
+ <a href="https://policies.google.com/technologies/ads" className="text-blue-500 underline" target="_blank" rel="noopener noreferrer">
136
+ Google Ad Policies
137
+ </a>.
138
+ </p>
139
+
140
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Third-Party Privacy Policies:</h2>
141
+ <p className={`${textSecondary} text-lg md:text-xl`}>
142
+ Humanizer X's Privacy Policy does not apply to other advertisers or websites. Users should review third-party privacy policies for detailed practices.
143
+ </p>
144
+
145
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>CCPA & GDPR Rights:</h2>
146
+ <p className={`${textSecondary} text-lg md:text-xl`}>
147
+ Users may request access, deletion, or restriction of their personal data under CCPA and GDPR. For more details or to exercise these rights, please contact us.
148
+ </p>
149
+
150
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>Children's Information:</h2>
151
+ <p className={`${textSecondary} text-lg md:text-xl`}>
152
+ Humanizer X does not knowingly collect personal information from children under 13. Parents should monitor children's online activity. If a child provides personal info, contact us immediately for removal.
153
+ </p>
154
+
155
+ <p className={`${textSecondary} text-lg md:text-xl`}>
156
+ For full details, you can always reach us at{" "}
157
+ <a
158
+ href="https://mail.google.com/mail/?view=cm&fs=1&to=humanizerx@gmail.com"
159
+ target="_blank"
160
+ rel="noopener noreferrer"
161
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
162
+ >
163
+ humanizerx@gmail.com
164
+ </a>.
165
+ </p>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </section>
170
+
171
+ <Footer />
172
+ </div>
173
+ );
174
+ };
175
+
176
+ export default PrivacyPolicyPage;
src/pages/ProfilePage.tsx ADDED
@@ -0,0 +1,444 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/ProfilePage.tsx
2
+ // ============================================================================
3
+ // ProfilePage Component (Mobile Friendly)
4
+ // ============================================================================
5
+ // ✅ This is a rewritten version of your ProfilePage with full mobile support.
6
+ // ✅ Functionality (Firestore, Auth, Tabs, Components, etc.) remains unchanged.
7
+ // ✅ Only layout fixes were made for mobile screens.
8
+ // ✅ Sidebar hidden on mobile, replaced with horizontal tab bar.
9
+ // ✅ Responsive spacing, padding, and typography.
10
+ // ============================================================================
11
+
12
+ import React, { useState, useEffect, useContext } from "react";
13
+ import { User, BarChart3, Settings, Activity } from "lucide-react";
14
+ import { auth, db } from "../firebase";
15
+ import { onAuthStateChanged, deleteUser } from "firebase/auth";
16
+ import {
17
+ collection,
18
+ increment,
19
+ doc,
20
+ setDoc,
21
+ addDoc,
22
+ query,
23
+ where,
24
+ orderBy,
25
+ getDocs,
26
+ Timestamp,
27
+ deleteDoc,
28
+ } from "firebase/firestore";
29
+ import { useNavigate } from "react-router-dom";
30
+
31
+ // --- Local Imports ---
32
+ import Header from "../components/Header";
33
+ import Footer from "../components/Footer";
34
+ import ProfileHeader from "../components/ProfileHeader";
35
+ import StatsGrid from "../components/StatsGrid";
36
+ import ActivityFeed from "../components/ActivityFeed";
37
+ import SettingsPanel from "../components/SettingsPanel";
38
+ import AnalyticsCharts from "../components/AnalyticsCharts";
39
+ import { Helmet } from "react-helmet-async";
40
+
41
+ // --- Types ---
42
+ import { UserProfile, UserActivity, UserPreferences } from "../types/user";
43
+ import { ThemeContext } from "../contexts/ThemeContext";
44
+
45
+ // ============================================================================
46
+ // ProfilePage Component
47
+ // ============================================================================
48
+ const ProfilePage: React.FC = () => {
49
+ // --- Navigation Hook ---
50
+ const navigate = useNavigate();
51
+
52
+ // --- Theme Context ---
53
+ const { theme, preference, setPreference, toggleTheme } =
54
+ useContext(ThemeContext);
55
+
56
+ // --- State ---
57
+ const [activeTab, setActiveTab] = useState<
58
+ "overview" | "analytics" | "activity" | "settings"
59
+ >("overview");
60
+
61
+ const [isEditing, setIsEditing] = useState(false);
62
+ const [user, setUser] = useState<UserProfile | null>(null);
63
+ const [editForm, setEditForm] = useState({ name: "", nickname: "", bio: "" });
64
+ const [activities, setActivities] = useState<UserActivity[]>([]);
65
+ const [loading, setLoading] = useState(true);
66
+
67
+ // ============================================================================
68
+ // Auth Handlers
69
+ // ============================================================================
70
+ const handleSignOut = async () => {
71
+ try {
72
+ await auth.signOut();
73
+ console.log("✅ Signed out successfully");
74
+ navigate("/login");
75
+ } catch (error) {
76
+ console.error("❌ Sign out failed:", error);
77
+ alert("Sign out failed, try again.");
78
+ }
79
+ };
80
+
81
+ const handleDeleteAccount = async () => {
82
+ try {
83
+ const currentUser = auth.currentUser;
84
+ if (!currentUser) throw new Error("No user logged in");
85
+
86
+ await deleteDoc(doc(db, "users", currentUser.uid));
87
+ await deleteUser(currentUser);
88
+
89
+ console.log("✅ Account deleted successfully");
90
+ navigate("/signup");
91
+ } catch (error: any) {
92
+ console.error("❌ Delete account failed:", error.message);
93
+ alert(`Delete account failed: ${error.message}`);
94
+ }
95
+ };
96
+
97
+ // ============================================================================
98
+ // Fetch User Profile
99
+ // ============================================================================
100
+ useEffect(() => {
101
+ const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
102
+ if (!currentUser) {
103
+ navigate("/signup");
104
+ return;
105
+ }
106
+
107
+ try {
108
+ const userQuery = query(
109
+ collection(db, "users"),
110
+ where("email", "==", currentUser.email)
111
+ );
112
+ const snapshot = await getDocs(userQuery);
113
+
114
+ if (!snapshot.empty) {
115
+ const data = snapshot.docs[0].data() as UserProfile;
116
+ setUser(data);
117
+ setEditForm({
118
+ name: data.name,
119
+ nickname: data.nickname,
120
+ bio: data.bio,
121
+ });
122
+
123
+ if (data.preferences?.theme) {
124
+ setPreference(data.preferences.theme);
125
+ }
126
+ } else {
127
+ const joinDate = currentUser.metadata?.creationTime
128
+ ? new Date(currentUser.metadata.creationTime).toLocaleDateString()
129
+ : new Date().toLocaleDateString();
130
+
131
+ const defaultPreferences: UserPreferences = {
132
+ theme: "dark",
133
+ language: "en",
134
+ timezone: "UTC",
135
+ emailNotifications: true,
136
+ pushNotifications: true,
137
+ marketingEmails: false,
138
+ twoFactorEnabled: false,
139
+ profileVisibility: "public",
140
+ };
141
+
142
+ const profile: UserProfile = {
143
+ id: currentUser.uid,
144
+ name: currentUser.displayName || "User",
145
+ nickname: currentUser.displayName?.split(" ")[0] || "User",
146
+ username: currentUser.email?.split("@")[0] || "username",
147
+ bio: "Welcome to my profile! I'm excited to be here.",
148
+ email: currentUser.email || "user@example.com",
149
+ avatar:
150
+ currentUser.photoURL ||
151
+ `https://images.pexels.com/photos/220453/pexels-photo-220453.jpeg?auto=compress&cs=tinysrgb&w=150&h=150&fit=crop`,
152
+ joinDate,
153
+ stats: {
154
+ textsHumanized: 0,
155
+ wordsProcessed: 0,
156
+ gptAverage: 0,
157
+ totalSessions: 0,
158
+ averageSessionTime: 0,
159
+ },
160
+ usernameChanged: false,
161
+ preferences: defaultPreferences,
162
+ lastActive: "Just now",
163
+ };
164
+
165
+ await setDoc(doc(db, "users", currentUser.uid), profile);
166
+ setUser(profile);
167
+ setEditForm({
168
+ name: profile.name,
169
+ nickname: profile.nickname,
170
+ bio: profile.bio,
171
+ });
172
+
173
+ setPreference(profile.preferences.theme);
174
+ await logActivity("Account Created", currentUser.email);
175
+ }
176
+
177
+ if (currentUser.email) {
178
+ await fetchActivities(currentUser.email);
179
+ }
180
+ } catch (error) {
181
+ console.error("Error loading user:", error);
182
+ } finally {
183
+ setLoading(false);
184
+ }
185
+ });
186
+
187
+ return () => unsubscribe();
188
+ }, [navigate, setPreference]);
189
+
190
+ // ============================================================================
191
+ // Activities
192
+ // ============================================================================
193
+ const fetchActivities = async (email: string) => {
194
+ try {
195
+ const q = query(
196
+ collection(db, "user_activities"),
197
+ where("email", "==", email),
198
+ orderBy("timestamp", "desc")
199
+ );
200
+ const snapshot = await getDocs(q);
201
+ const acts: UserActivity[] = snapshot.docs.map((doc) => {
202
+ const data = doc.data();
203
+ return {
204
+ id: doc.id,
205
+ action: data.action,
206
+ timestamp:
207
+ data.timestamp instanceof Timestamp
208
+ ? data.timestamp.toDate().toLocaleString()
209
+ : data.timestamp,
210
+ details: data.details || "",
211
+ type: data.type || "profile",
212
+ };
213
+ });
214
+ setActivities(acts);
215
+ } catch (error) {
216
+ console.error("Fetch activities error:", error);
217
+ }
218
+ };
219
+
220
+ const logActivity = async (action: string, email?: string) => {
221
+ if (!email) return;
222
+ try {
223
+ await addDoc(collection(db, "user_activities"), {
224
+ email,
225
+ action,
226
+ timestamp: Timestamp.fromDate(new Date()),
227
+ });
228
+ } catch (error) {
229
+ console.error("Activity log error:", error);
230
+ }
231
+ };
232
+
233
+ // ============================================================================
234
+ // Edit Profile
235
+ // ============================================================================
236
+ const handleSave = async () => {
237
+ if (!user) return;
238
+ const updated: UserProfile = {
239
+ ...user,
240
+ name: editForm.name,
241
+ nickname: editForm.nickname,
242
+ bio: editForm.bio,
243
+ lastActive: "Just now",
244
+ };
245
+ setUser(updated);
246
+ setIsEditing(false);
247
+ await setDoc(doc(db, "users", auth.currentUser!.uid), updated, {
248
+ merge: true,
249
+ });
250
+ await logActivity("Profile Updated", user.email);
251
+ if (user.email) await fetchActivities(user.email);
252
+ };
253
+
254
+ const handleCancel = () => {
255
+ if (user)
256
+ setEditForm({
257
+ name: user.name,
258
+ nickname: user.nickname,
259
+ bio: user.bio,
260
+ });
261
+ setIsEditing(false);
262
+ };
263
+
264
+ const handleAvatarChange = () => console.log("Avatar change clicked");
265
+
266
+ // ============================================================================
267
+ // Tabs
268
+ // ============================================================================
269
+ const tabs = [
270
+ { id: "overview", label: "Overview", icon: User },
271
+ { id: "analytics", label: "Analytics", icon: BarChart3 },
272
+ { id: "activity", label: "Activity", icon: Activity },
273
+ { id: "settings", label: "Settings", icon: Settings },
274
+ ];
275
+
276
+ // ============================================================================
277
+ // Render Tab Content
278
+ // ============================================================================
279
+ const renderContent = () => {
280
+ if (!user) return null;
281
+
282
+ const commonProps = {
283
+ theme,
284
+ preference,
285
+ onThemeChange: setPreference,
286
+ onThemeToggle: toggleTheme,
287
+ user,
288
+ onUpdatePreferences: async (prefs: Partial<UserPreferences>) => {
289
+ setUser((prev) =>
290
+ prev
291
+ ? { ...prev, preferences: { ...prev.preferences, ...prefs } }
292
+ : prev
293
+ );
294
+
295
+ if (prefs.theme) setPreference(prefs.theme);
296
+
297
+ await setDoc(
298
+ doc(db, "users", auth.currentUser!.uid),
299
+ { preferences: { ...user?.preferences, ...prefs } },
300
+ { merge: true }
301
+ );
302
+ },
303
+ onSignOut: handleSignOut,
304
+ onDeleteAccount: handleDeleteAccount,
305
+ };
306
+
307
+ switch (activeTab) {
308
+ case "overview":
309
+ return (
310
+ <div className="space-y-6">
311
+ <ProfileHeader
312
+ {...commonProps}
313
+ isEditing={isEditing}
314
+ editForm={editForm}
315
+ setEditForm={setEditForm}
316
+ onEdit={() => setIsEditing(true)}
317
+ onSave={handleSave}
318
+ onCancel={handleCancel}
319
+ onAvatarChange={handleAvatarChange}
320
+ />
321
+ <StatsGrid stats={user.stats} theme={theme} />
322
+ <ActivityFeed activities={activities} theme={theme} />
323
+ </div>
324
+ );
325
+ case "analytics":
326
+ return <AnalyticsCharts stats={user.stats} theme={theme} />;
327
+ case "activity":
328
+ return <ActivityFeed activities={activities} theme={theme} />;
329
+ case "settings":
330
+ return <SettingsPanel {...commonProps} />;
331
+ default:
332
+ return null;
333
+ }
334
+ };
335
+
336
+ // ============================================================================
337
+ // Loading Spinner
338
+ // ============================================================================
339
+ if (loading)
340
+ return (
341
+ <div className="min-h-screen flex items-center justify-center">
342
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
343
+ </div>
344
+ );
345
+
346
+ // ============================================================================
347
+ // Render Component
348
+ // ============================================================================
349
+ return (
350
+ <div
351
+ className={`${
352
+ theme === "dark"
353
+ ? "bg-gray-900 text-white"
354
+ : "bg-gray-50 text-gray-900"
355
+ } min-h-screen`}
356
+ >
357
+ {/* ✅ SEO + Canonical */}
358
+ <Helmet>
359
+ <title>Your Profile – Manage Your Account | HumanizerX</title>
360
+ <meta
361
+ name="description"
362
+ content="Access and manage your Humanizer X profile. View activity, track stats, adjust preferences, and personalize your AI humanization experience."
363
+ />
364
+ <link
365
+ rel="canonical"
366
+ href="https://www.humanizerx.pro/profile"
367
+ />
368
+ </Helmet>
369
+ {/* Header */}
370
+ <Header theme={theme} onThemeToggle={toggleTheme} />
371
+
372
+ <div className="flex flex-col md:flex-row">
373
+ {/* Sidebar - Desktop Only */}
374
+ <aside
375
+ className={`hidden md:block w-64 min-h-screen sticky top-0 p-6 ${
376
+ theme === "dark"
377
+ ? "bg-gray-800 text-white border-r border-gray-700"
378
+ : "bg-white text-gray-900 border-r border-blue-300"
379
+ }`}
380
+ >
381
+ <nav className="space-y-2">
382
+ {tabs.map((tab) => {
383
+ const Icon = tab.icon;
384
+ return (
385
+ <button
386
+ key={tab.id}
387
+ onClick={() => setActiveTab(tab.id as any)}
388
+ className={`w-full flex items-center gap-3 px-4 py-3 text-sm font-medium rounded-lg transition ${
389
+ activeTab === tab.id
390
+ ? "bg-blue-50 text-blue-700 border border-blue-200"
391
+ : `${
392
+ theme === "dark"
393
+ ? "text-white hover:bg-gray-700"
394
+ : "text-gray-900 hover:bg-gray-200"
395
+ }`
396
+ }`}
397
+ >
398
+ <Icon className="w-5 h-5" />
399
+ {tab.label}
400
+ </button>
401
+ );
402
+ })}
403
+ </nav>
404
+ </aside>
405
+
406
+ {/* Mobile Tabs */}
407
+ <div className="md:hidden sticky top-0 z-10 bg-inherit border-b border-gray-200 dark:border-gray-700">
408
+ <div className="flex overflow-x-auto">
409
+ {tabs.map((tab) => {
410
+ const Icon = tab.icon;
411
+ return (
412
+ <button
413
+ key={tab.id}
414
+ onClick={() => setActiveTab(tab.id as any)}
415
+ className={`flex-1 flex items-center justify-center gap-2 px-4 py-3 text-sm font-medium ${
416
+ activeTab === tab.id
417
+ ? "text-blue-600 border-b-2 border-blue-600"
418
+ : "text-gray-600 dark:text-gray-300 hover:text-blue-600"
419
+ }`}
420
+ >
421
+ <Icon className="w-4 h-4" />
422
+ {tab.label}
423
+ </button>
424
+ );
425
+ })}
426
+ </div>
427
+ </div>
428
+
429
+ {/* Main Content */}
430
+ <main className="flex-1 p-4 md:p-6">
431
+ <div className="max-w-6xl mx-auto">{renderContent()}</div>
432
+ </main>
433
+ </div>
434
+
435
+ {/* Footer */}
436
+ <Footer theme={theme} />
437
+ </div>
438
+ );
439
+ };
440
+
441
+ // ============================================================================
442
+ // Export
443
+ // ====================================
444
+ export default ProfilePage;
src/pages/SignupPage.tsx ADDED
@@ -0,0 +1,400 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/SignupPage.tsx
2
+ import React, { useState, useContext } from 'react';
3
+ import { ThemeContext } from '../contexts/ThemeContext';
4
+ import { Link, useNavigate } from "react-router-dom";
5
+ import {
6
+ Mail,
7
+ Lock,
8
+ User,
9
+ Eye,
10
+ EyeOff,
11
+ CheckCircle,
12
+ Loader2,
13
+ ArrowLeft,
14
+ } from "lucide-react";
15
+ import { auth, provider } from "../firebase";
16
+ import {
17
+ createUserWithEmailAndPassword,
18
+ updateProfile,
19
+ sendEmailVerification,
20
+ signInWithPopup,
21
+ } from "firebase/auth";
22
+ import Header from "../components/Header";
23
+ import Footer from "../components/Footer";
24
+
25
+ const SignupPage: React.FC = () => {
26
+ const navigate = useNavigate();
27
+
28
+ const { theme } = useContext(ThemeContext);
29
+ const [formData, setFormData] = useState({
30
+ firstName: "",
31
+ lastName: "",
32
+ email: "",
33
+ password: "",
34
+ confirmPassword: "",
35
+ });
36
+ const [showPassword, setShowPassword] = useState(false);
37
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
38
+ const [isLoading, setIsLoading] = useState(false);
39
+ const [agreedToTerms, setAgreedToTerms] = useState(false);
40
+ const [error, setError] = useState("");
41
+
42
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
43
+ const { name, value } = e.target;
44
+ setFormData((prev) => ({
45
+ ...prev,
46
+ [name]: value,
47
+ }));
48
+ };
49
+
50
+ // ✅ Email/Password Signup
51
+ const handleSubmit = async (e: React.FormEvent) => {
52
+ e.preventDefault();
53
+ setError("");
54
+
55
+ if (formData.password !== formData.confirmPassword) {
56
+ setError("Passwords do not match.");
57
+ return;
58
+ }
59
+ if (!agreedToTerms) {
60
+ setError("Please agree to the terms and conditions.");
61
+ return;
62
+ }
63
+
64
+ try {
65
+ setIsLoading(true);
66
+ const userCredential = await createUserWithEmailAndPassword(
67
+ auth,
68
+ formData.email,
69
+ formData.password
70
+ );
71
+
72
+ // ✅ Update profile with full name
73
+ await updateProfile(userCredential.user, {
74
+ displayName: `${formData.firstName} ${formData.lastName}`,
75
+ });
76
+
77
+ // ✅ Send Email Verification
78
+ await sendEmailVerification(userCredential.user);
79
+
80
+ // ✅ Redirect to Thank You page
81
+ navigate("/thank-you");
82
+ } catch (err: any) {
83
+ setError(err.message);
84
+ } finally {
85
+ setIsLoading(false);
86
+ }
87
+ };
88
+
89
+ // ✅ Google Signup
90
+ const handleGoogleSignup = async () => {
91
+ setError("");
92
+ try {
93
+ setIsLoading(true);
94
+ const result = await signInWithPopup(auth, provider);
95
+
96
+ if (!result.user.emailVerified) {
97
+ await auth.signOut();
98
+ setError("Please verify your Google email before continuing.");
99
+ return;
100
+ }
101
+
102
+ // ✅ Redirect verified Google users
103
+ navigate("/thank-you");
104
+ } catch (err: any) {
105
+ setError(err.message);
106
+ } finally {
107
+ setIsLoading(false);
108
+ }
109
+ };
110
+
111
+ return (
112
+ <div className={`${theme} min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors duration-300`}>
113
+ <Header />
114
+
115
+ <main className="container mx-auto px-4 py-8 flex items-center justify-center min-h-[calc(100vh-200px)]">
116
+ <div className="w-full max-w-md">
117
+ <div className="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-8 relative">
118
+ {/* 🔙 Back Button */}
119
+ <button
120
+ onClick={() => navigate(-1)}
121
+ className="absolute top-4 left-4 text-gray-500 hover:text-gray-900 dark:hover:text-white"
122
+ >
123
+ <ArrowLeft className="w-5 h-5" />
124
+ </button>
125
+
126
+ {/* Logo and Title */}
127
+ <div className="text-center mb-8">
128
+ <img
129
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
130
+ alt="Humanizer X Signup - Create Account Logo"
131
+ title="Create your Humanizer X account - Join our AI text humanization platform"
132
+ className="w-16 h-16 mx-auto mb-4 rounded-lg"
133
+ />
134
+ <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
135
+ Create Account
136
+ </h1>
137
+ <p className="text-gray-600 dark:text-gray-300">
138
+ Join Humanizer X and start transforming your content
139
+ </p>
140
+ </div>
141
+
142
+ {/* Error Message */}
143
+ {error && (
144
+ <div className="mb-4 p-3 text-sm text-red-600 bg-red-100 dark:bg-red-900/40 dark:text-red-400 rounded-lg">
145
+ {error}
146
+ </div>
147
+ )}
148
+
149
+ {/* Google Signup */}
150
+ <button
151
+ onClick={handleGoogleSignup}
152
+ disabled={isLoading}
153
+ className="w-full mb-6 px-6 py-3 border border-gray-300 dark:border-gray-600 rounded-lg flex items-center justify-center gap-3 hover:bg-gray-100 dark:hover:bg-gray-700 transition-all"
154
+ >
155
+ <img
156
+ src="https://www.svgrepo.com/show/475656/google-color.svg"
157
+ alt="Google Logo"
158
+ className="w-5 h-5"
159
+ />
160
+ <span className="text-gray-700 dark:text-gray-200 font-medium">
161
+ Continue with Google
162
+ </span>
163
+ </button>
164
+
165
+ {/* Signup Form */}
166
+ <form onSubmit={handleSubmit} className="space-y-6">
167
+ <div className="grid grid-cols-2 gap-4">
168
+ {/* First Name */}
169
+ <div>
170
+ <label
171
+ htmlFor="firstName"
172
+ className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
173
+ >
174
+ First Name
175
+ </label>
176
+ <div className="relative">
177
+ <User className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
178
+ <input
179
+ type="text"
180
+ id="firstName"
181
+ name="firstName"
182
+ value={formData.firstName}
183
+ onChange={handleInputChange}
184
+ required
185
+ className="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
186
+ placeholder="John"
187
+ />
188
+ </div>
189
+ </div>
190
+
191
+ {/* Last Name */}
192
+ <div>
193
+ <label
194
+ htmlFor="lastName"
195
+ className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
196
+ >
197
+ Last Name
198
+ </label>
199
+ <input
200
+ type="text"
201
+ id="lastName"
202
+ name="lastName"
203
+ value={formData.lastName}
204
+ onChange={handleInputChange}
205
+ required
206
+ className="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
207
+ placeholder="Doe"
208
+ />
209
+ </div>
210
+ </div>
211
+
212
+ {/* Email */}
213
+ <div>
214
+ <label
215
+ htmlFor="email"
216
+ className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
217
+ >
218
+ Email Address
219
+ </label>
220
+ <div className="relative">
221
+ <Mail className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
222
+ <input
223
+ type="email"
224
+ id="email"
225
+ name="email"
226
+ value={formData.email}
227
+ onChange={handleInputChange}
228
+ required
229
+ className="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
230
+ placeholder="john@example.com"
231
+ />
232
+ </div>
233
+ </div>
234
+
235
+ {/* Password */}
236
+ <div>
237
+ <label
238
+ htmlFor="password"
239
+ className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
240
+ >
241
+ Password
242
+ </label>
243
+ <div className="relative">
244
+ <Lock className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
245
+ <input
246
+ type={showPassword ? "text" : "password"}
247
+ id="password"
248
+ name="password"
249
+ value={formData.password}
250
+ onChange={handleInputChange}
251
+ required
252
+ className="w-full pl-10 pr-12 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
253
+ placeholder="Create a strong password"
254
+ />
255
+ <button
256
+ type="button"
257
+ onClick={() => setShowPassword(!showPassword)}
258
+ className="absolute inset-y-0 right-3 flex items-center"
259
+ >
260
+ {showPassword ? (
261
+ <EyeOff className="h-5 w-5 text-gray-400" />
262
+ ) : (
263
+ <Eye className="h-5 w-5 text-gray-400" />
264
+ )}
265
+ </button>
266
+ </div>
267
+ </div>
268
+
269
+ {/* Confirm Password */}
270
+ <div>
271
+ <label
272
+ htmlFor="confirmPassword"
273
+ className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
274
+ >
275
+ Confirm Password
276
+ </label>
277
+ <div className="relative">
278
+ <Lock className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
279
+ <input
280
+ type={showConfirmPassword ? "text" : "password"}
281
+ id="confirmPassword"
282
+ name="confirmPassword"
283
+ value={formData.confirmPassword}
284
+ onChange={handleInputChange}
285
+ required
286
+ className="w-full pl-10 pr-12 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
287
+ placeholder="Confirm your password"
288
+ />
289
+ <button
290
+ type="button"
291
+ onClick={() => setShowConfirmPassword(!showConfirmPassword)}
292
+ className="absolute inset-y-0 right-3 flex items-center"
293
+ >
294
+ {showConfirmPassword ? (
295
+ <EyeOff className="h-5 w-5 text-gray-400" />
296
+ ) : (
297
+ <Eye className="h-5 w-5 text-gray-400" />
298
+ )}
299
+ </button>
300
+ </div>
301
+ </div>
302
+
303
+ {/* Terms */}
304
+ <div className="flex items-center">
305
+ <input
306
+ id="terms"
307
+ name="terms"
308
+ type="checkbox"
309
+ checked={agreedToTerms}
310
+ onChange={(e) => setAgreedToTerms(e.target.checked)}
311
+ className="h-4 w-4 text-blue-600 border-gray-300 rounded"
312
+ />
313
+ <label
314
+ htmlFor="terms"
315
+ className="ml-2 block text-sm text-gray-700 dark:text-gray-300"
316
+ >
317
+ I agree to the{" "}
318
+ <a
319
+ href="/terms"
320
+ className="text-blue-600 dark:text-blue-400 hover:underline"
321
+ >
322
+ Terms & Conditions
323
+ </a>{" "}
324
+ and{" "}
325
+ <a
326
+ href="/privacy-policy"
327
+ className="text-blue-600 dark:text-blue-400 hover:underline"
328
+ >
329
+ Privacy Policy
330
+ </a>
331
+ </label>
332
+ </div>
333
+
334
+ {/* Submit */}
335
+ <button
336
+ type="submit"
337
+ disabled={isLoading || !agreedToTerms}
338
+ className="w-full px-6 py-3 bg-gradient-to-r from-indigo-800 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center gap-2"
339
+ >
340
+ {isLoading ? (
341
+ <>
342
+ <Loader2 className="animate-spin w-4 h-4" />
343
+ Creating Account...
344
+ </>
345
+ ) : (
346
+ <>
347
+ <CheckCircle size={16} />
348
+ Create Account
349
+ </>
350
+ )}
351
+ </button>
352
+ </form>
353
+
354
+ {/* Login Link */}
355
+ <div className="mt-6 text-center">
356
+ <p className="text-gray-600 dark:text-gray-300">
357
+ Already have an account?{" "}
358
+ <Link
359
+ to="/login"
360
+ className="text-blue-600 dark:text-blue-400 font-medium hover:underline"
361
+ >
362
+ Sign in here
363
+ </Link>
364
+ </p>
365
+ </div>
366
+ </div>
367
+
368
+ {/* Benefits */}
369
+ <div className="mt-6 bg-gradient-to-br from-blue-50 to-purple-50 dark:from-blue-900/20 dark:to-purple-900/20 rounded-xl p-6 border border-blue-200 dark:border-blue-800">
370
+ <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-3">
371
+ What you get with your free account:
372
+ </h3>
373
+ <ul className="space-y-2 text-sm text-gray-600 dark:text-gray-300">
374
+ <li className="flex items-center gap-2">
375
+ <CheckCircle className="text-green-600 w-4 h-4" />
376
+ Up to 6,000 words per humanization
377
+ </li>
378
+ <li className="flex items-center gap-2">
379
+ <CheckCircle className="text-green-600 w-4 h-4" />
380
+ 10,000 words for GPT detection
381
+ </li>
382
+ <li className="flex items-center gap-2">
383
+ <CheckCircle className="text-green-600 w-4 h-4" />
384
+ File upload support (PDF, Word, Text)
385
+ </li>
386
+ <li className="flex items-center gap-2">
387
+ <CheckCircle className="text-green-600 w-4 h-4" />
388
+ Advanced plagiarism detection
389
+ </li>
390
+ </ul>
391
+ </div>
392
+ </div>
393
+ </main>
394
+
395
+ <Footer />
396
+ </div>
397
+ );
398
+ };
399
+
400
+ export default SignupPage;
src/pages/Terms.tsx ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useContext } from "react";
2
+ import { ThemeContext } from "../contexts/ThemeContext";
3
+ import Header from "../components/Header";
4
+ import Footer from "../components/Footer";
5
+ import { Helmet } from "react-helmet-async";
6
+
7
+ const Terms: React.FC = () => {
8
+ const { theme } = useContext(ThemeContext);
9
+
10
+ const textPrimary = theme === "dark" ? "text-white" : "text-gray-900";
11
+ const textSecondary = theme === "dark" ? "text-gray-300" : "text-gray-600";
12
+
13
+ return (
14
+ <div
15
+ className={`min-h-screen transition-colors duration-300 ${
16
+ theme === "dark" ? "bg-gray-900" : "bg-gray-50"
17
+ }`}
18
+ >
19
+ {/* ✅ SEO + Canonical */}
20
+ <Helmet>
21
+ <title>Terms & Conditions – User Agreement | HumanizerX</title>
22
+ <meta
23
+ name="description"
24
+ content="Read the official Terms and Conditions of Humanizer X. Understand the rules, policies, and user rights for using our AI humanization services."
25
+ />
26
+ <link
27
+ rel="canonical"
28
+ href="https://www.humanizerx.pro/terms"
29
+ />
30
+ </Helmet>
31
+ <Header />
32
+
33
+ <section className="py-16 mb-16">
34
+ <div className="container mx-auto px-4 max-w-6xl">
35
+ <div
36
+ className={`${
37
+ theme === "dark"
38
+ ? "bg-gray-800 border-gray-700"
39
+ : "bg-white border-gray-200"
40
+ } rounded-xl shadow-lg border p-10 space-y-8`}
41
+ >
42
+ {/* Logo + Title */}
43
+ <div className="text-center">
44
+ <img
45
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
46
+ alt="Humanizer X Terms and Conditions - Legal Information Logo"
47
+ title="Humanizer X Terms & Conditions - Read our service terms"
48
+ className="mx-auto w-40 h-30 object-contain mb-6"
49
+ />
50
+ <h1 className={`text-4xl md:text-5xl font-bold mb-4 ${textPrimary}`}>
51
+ Terms & Conditions
52
+ </h1>
53
+ </div>
54
+
55
+ {/* Terms Content */}
56
+ <div className="space-y-6">
57
+ <p className={`${textSecondary} text-lg md:text-xl`}>
58
+ Welcome to{" "}
59
+ <a
60
+ href="https://www.humanizerx.pro/"
61
+ target="_blank"
62
+ rel="noopener noreferrer"
63
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
64
+ >
65
+ Humanizer X
66
+ </a>
67
+ ! These terms and conditions outline the rules and regulations
68
+ for the use of Humanizer X. By accessing this website, we assume
69
+ you accept these terms and conditions. Do not continue to use
70
+ Humanizer X if you do not agree with the terms on this page.
71
+ </p>
72
+
73
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>
74
+ Terminology:
75
+ </h2>
76
+ <p className={`${textSecondary} text-lg md:text-xl`}>
77
+ The following terminology applies to these Terms and Conditions,
78
+ Privacy Statement, and Disclaimer Notice and all Agreements:
79
+ &quot;Client&quot;, &quot;You&quot; and &quot;Your&quot; refer
80
+ to you, the person logging on to this website and compliant to
81
+ the Company’s terms and conditions. &quot;The Company&quot;,
82
+ &quot;Ourselves&quot;, &quot;We&quot;, &quot;Our&quot;, and
83
+ &quot;Us&quot; refers to our Company. &quot;Party&quot;,
84
+ &quot;Parties&quot;, or &quot;Us&quot; refers to both the Client
85
+ and ourselves.
86
+ </p>
87
+
88
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>
89
+ Cookies:
90
+ </h2>
91
+ <p className={`${textSecondary} text-lg md:text-xl`}>
92
+ We employ the use of cookies. By accessing Humanizer X, you
93
+ agree to use cookies in agreement with{" "}
94
+ <a
95
+ href="/privacy-policy"
96
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
97
+ >
98
+ Humanizer X’s Privacy Policy:
99
+ </a>
100
+ . Most interactive websites use cookies to let us retrieve the
101
+ user’s details for each visit. Cookies are used by our website
102
+ to enable certain functionality and improve user experience.
103
+ Some of our affiliate/advertising partners may also use cookies.
104
+ </p>
105
+
106
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>
107
+ Content Liability:
108
+ </h2>
109
+ <p className={`${textSecondary} text-lg md:text-xl`}>
110
+ We shall not be held responsible for any content that appears on
111
+ your Website. You agree to protect and defend us against all
112
+ claims that arise on your Website. No link(s) should appear on
113
+ any Website that may be interpreted as libelous, obscene, or
114
+ criminal, or which infringes, otherwise violates, or advocates
115
+ the infringement of, any third-party rights.
116
+ </p>
117
+
118
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>
119
+ Your Privacy:
120
+ </h2>
121
+ <p className={`${textSecondary} text-lg md:text-xl`}>
122
+ Please read our{" "}
123
+ <a
124
+ href="/privacy-policy"
125
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
126
+ >
127
+ Privacy Policy
128
+ </a>
129
+ .
130
+ </p>
131
+
132
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>
133
+ Reservation of Rights:
134
+ </h2>
135
+ <p className={`${textSecondary} text-lg md:text-xl`}>
136
+ We reserve the right to request that you remove all links or any
137
+ particular link to our Website. You agree to immediately remove
138
+ all links to our Website upon request. We also reserve the right
139
+ to amend these terms and conditions and our linking policy at
140
+ any time. By continuously linking to our Website, you agree to
141
+ be bound to and follow these linking terms and conditions.
142
+ </p>
143
+
144
+ <h2 className={`text-2xl md:text-3xl font-bold ${textPrimary}`}>
145
+ Removal of Links:
146
+ </h2>
147
+ <p className={`${textSecondary} text-lg md:text-xl`}>
148
+ If you find any link on our Website that is offensive for any reason, feel free to{" "}
149
+ <a
150
+ href="/contact"
151
+ className="text-blue-500 underline hover:text-blue-600 transition-colors"
152
+ >
153
+ contact us
154
+ </a>{" "}
155
+ at any moment. We will consider requests to remove links, but we are not obligated
156
+ to or to respond to you directly. We do not ensure that the information on this
157
+ website is correct, we do not warrant its completeness or accuracy, nor do we promise
158
+ to ensure that the website remains available or that the information on the website
159
+ is kept up to date.
160
+ </p>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </section>
165
+
166
+ <Footer />
167
+ </div>
168
+ );
169
+ };
170
+
171
+ export default Terms;
src/pages/ThankYouPage.tsx ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/ThankYouPage.tsx
2
+ import React, { useEffect, useState, useContext } from "react";
3
+ import { ThemeContext } from "../contexts/ThemeContext";
4
+ import { Link, useNavigate } from "react-router-dom";
5
+ import { CheckCircle } from "lucide-react";
6
+ import Header from "../components/Header";
7
+ import Footer from "../components/Footer";
8
+ import { auth } from "../firebase";
9
+ import { onAuthStateChanged, User } from "firebase/auth";
10
+
11
+ const ThankYouPage: React.FC = () => {
12
+ const [user, setUser] = useState<User | null>(null);
13
+ const navigate = useNavigate();
14
+ const { theme } = useContext(ThemeContext);
15
+
16
+ useEffect(() => {
17
+ const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
18
+ setUser(currentUser);
19
+ });
20
+ return () => unsubscribe();
21
+ }, []);
22
+
23
+ // Determine which button to show
24
+ const renderButton = () => {
25
+ if (!user) {
26
+ // User not signed in
27
+ return (
28
+ <Link
29
+ to="/login"
30
+ className="inline-block px-6 py-3 bg-gradient-to-r from-indigo-800 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 transition"
31
+ >
32
+ Go to Login
33
+ </Link>
34
+ );
35
+ }
36
+
37
+ // User signed in, maybe with Google or other provider
38
+ return (
39
+ <button
40
+ onClick={() => navigate("/profile")} // redirect to app/home
41
+ className="inline-block px-6 py-3 bg-gradient-to-r from-indigo-800 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 transition"
42
+ >
43
+ Go to Profile
44
+ </button>
45
+ );
46
+ };
47
+
48
+ return (
49
+ <div className={`${theme} min-h-screen flex flex-col bg-gray-50 dark:bg-gray-900 transition-colors duration-300`}>
50
+ <Header />
51
+
52
+ <main className="flex-1 flex items-center justify-center px-4 py-12">
53
+ <div className="bg-white dark:bg-gray-800 shadow-lg rounded-2xl p-8 max-w-md w-full text-center">
54
+
55
+ {/* Logo */}
56
+ <img
57
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
58
+ alt="Humanizer X Thank You - Registration Success Logo"
59
+ title="Welcome to Humanizer X - Registration completed successfully"
60
+ className="w-20 h-20 mx-auto mb-6"
61
+ />
62
+
63
+ <CheckCircle className="w-16 h-16 text-green-500 mx-auto mb-4" />
64
+
65
+ {/* Heading */}
66
+ <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
67
+ Thank You for Registering!
68
+ </h1>
69
+
70
+ {/* Personalized Message */}
71
+ {user && (
72
+ <p className="text-lg font-medium text-indigo-700 dark:text-indigo-300 mb-4">
73
+ Welcome, {user.displayName || "User"} 🎉
74
+ </p>
75
+ )}
76
+ {user?.email && (
77
+ <p className="text-gray-700 dark:text-gray-400 mb-6">
78
+ Registered Email: <span className="font-semibold">{user.email}</span>
79
+ </p>
80
+ )}
81
+
82
+ {/* Info Text */}
83
+ <p className="text-gray-600 dark:text-gray-300 mb-6">
84
+ Your Humanizer X account has been created successfully.
85
+ {user?.providerData?.some(p => p.providerId === "google.com")
86
+ ? " You can continue directly to Humanizer X."
87
+ : " Please check your email inbox to verify your account before logging in."
88
+ }
89
+ </p>
90
+
91
+ {/* Conditional Button */}
92
+ {renderButton()}
93
+ </div>
94
+ </main>
95
+
96
+ <Footer />
97
+ </div>
98
+ );
99
+ };
100
+
101
+ export default ThankYouPage;
src/pages/WelcomePage.tsx ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // src/pages/WelcomePage.tsx
2
+ import React, { useEffect, useState, useContext } from "react";
3
+ import { ThemeContext } from "../contexts/ThemeContext";
4
+ import { LogIn } from "lucide-react";
5
+ import Header from "../components/Header";
6
+ import Footer from "../components/Footer";
7
+ import { auth } from "../firebase"; // ✅ Firebase import
8
+ import { onAuthStateChanged, User } from "firebase/auth";
9
+
10
+ const WelcomePage: React.FC = () => {
11
+ const [user, setUser] = useState<User | null>(null);
12
+ const { theme } = useContext(ThemeContext);
13
+
14
+ useEffect(() => {
15
+ // ✅ Listen for auth state
16
+ const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
17
+ setUser(currentUser);
18
+ });
19
+
20
+ return () => unsubscribe();
21
+ }, []);
22
+
23
+ return (
24
+ <div className={`${theme} min-h-screen flex flex-col bg-gray-50 dark:bg-gray-900 transition-colors duration-300`}>
25
+ <Header />
26
+
27
+ <main className="flex-1 flex items-center justify-center px-4 py-12">
28
+ <div className="bg-white dark:bg-gray-800 shadow-lg rounded-2xl p-8 max-w-md w-full text-center">
29
+
30
+ {/* ✅ Logo */}
31
+ <img
32
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
33
+ alt="Humanizer X Welcome - Successfully Logged In Logo"
34
+ title="Welcome back to Humanizer X - You are now signed in"
35
+ className="w-20 h-20 mx-auto mb-6"
36
+ />
37
+
38
+ {/* ✅ Icon */}
39
+ <LogIn className="w-16 h-16 text-indigo-600 mx-auto mb-4" />
40
+
41
+ {/* ✅ Heading */}
42
+ <h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
43
+ Welcome Back{user ? `, ${user.displayName || user.email}` : ""} 👋
44
+ </h1>
45
+
46
+ <p className="text-gray-600 dark:text-gray-300 mb-6">
47
+ You are now signed in to Humanizer X.
48
+ </p>
49
+
50
+ {/* ✅ HumanizerX Button */}
51
+ <button
52
+ className="inline-block px-6 py-3 bg-gradient-to-r from-indigo-800 to-slate-900 text-white rounded-lg font-medium hover:from-indigo-600 hover:to-slate-700 transition"
53
+ onClick={() => (window.location.href = "/")}
54
+ >
55
+ Humanizer X
56
+ </button>
57
+ </div>
58
+ </main>
59
+
60
+ <Footer />
61
+ </div>
62
+ );
63
+ };
64
+
65
+ export default WelcomePage;
src/pages/WordCounterPage.tsx ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useRef, useContext } from 'react';
2
+ import { Upload, AlertCircle, Copy, Shield, ChevronRight } from 'lucide-react';
3
+ import { Link } from 'react-router-dom';
4
+ import Header from '../components/Header';
5
+ import Footer from '../components/Footer';
6
+ import { ThemeContext } from '../contexts/ThemeContext';
7
+ import { Helmet } from "react-helmet-async";
8
+
9
+ const WordCounterPage: React.FC = () => {
10
+ const [text, setText] = useState('');
11
+ const [uploadedFileName, setUploadedFileName] = useState('');
12
+ const [error, setError] = useState('');
13
+ const [copied, setCopied] = useState(false);
14
+ const fileInputRef = useRef<HTMLInputElement>(null);
15
+ const { theme } = useContext(ThemeContext);
16
+
17
+ // --- Handlers ---
18
+ const handleTextChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
19
+ setText(e.target.value);
20
+ if (error) setError('');
21
+ };
22
+
23
+ const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
24
+ const file = e.target.files?.[0];
25
+ if (!file) return;
26
+ setUploadedFileName(file.name);
27
+
28
+ if (file.type === 'text/plain') {
29
+ const content = await file.text();
30
+ setText(content);
31
+ setError('');
32
+ } else {
33
+ try {
34
+ const formData = new FormData();
35
+ formData.append('file', file);
36
+ const response = await fetch('http://localhost:3001/api/upload-file', {
37
+ method: 'POST',
38
+ body: formData,
39
+ });
40
+ const data = await response.json();
41
+ if (data.success) {
42
+ setText(data.extracted_text);
43
+ setUploadedFileName(data.file_name);
44
+ setError('');
45
+ } else {
46
+ setError(data.error || 'Failed to process file');
47
+ }
48
+ } catch {
49
+ setError('Error uploading file');
50
+ }
51
+ }
52
+ };
53
+
54
+ const handleCopy = () => {
55
+ navigator.clipboard.writeText(text);
56
+ setCopied(true);
57
+ setTimeout(() => setCopied(false), 1500); // Reset after 1.5s
58
+ };
59
+
60
+ // --- Metrics ---
61
+ const wordCount = text.trim().split(/\s+/).filter(w => w.length > 0).length;
62
+ const charCount = text.length;
63
+ const sentenceCount = text.split(/[.!?]+/).filter(s => s.trim().length > 0).length;
64
+ const readingTime = Math.ceil(wordCount / 200);
65
+
66
+ // --- Theme Classes ---
67
+ const bgMain = theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-gray-50 text-gray-900';
68
+ const cardBg = theme === 'dark' ? 'bg-gray-800' : 'bg-white';
69
+ const secondaryText = theme === 'dark' ? 'text-gray-300' : 'text-gray-600';
70
+ const borderCard = theme === 'dark' ? 'border-gray-700' : 'border-gray-200';
71
+
72
+ return (
73
+ <div className={`min-h-screen transition-colors duration-300 ${theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-gray-50 text-gray-900'}`}>
74
+
75
+ {/* ✅ SEO + Canonical */}
76
+ <Helmet>
77
+ <title>Word Counter – Free Online Text & Character Counter | HumanizerX</title>
78
+ <meta
79
+ name="description"
80
+ content="Use HumanizerX Word Counter to instantly count words, characters, and sentences in your text. Perfect for writers, students, and professionals."
81
+ />
82
+ <link
83
+ rel="canonical"
84
+ href="https://www.humanizerx.pro/word-counter"
85
+ />
86
+ </Helmet>
87
+ <Header />
88
+
89
+ {/* Hero */}
90
+ <section className="bg-gradient-to-r from-indigo-900 to-slate-900 text-white py-20 text-center">
91
+ <div className="container mx-auto px-4 max-w-4xl">
92
+ <img
93
+ src="https://ucarecdn.com/baca8c8c-4a8b-4020-82e6-1382cdd3782a/-/format/auto/"
94
+ alt="Humanizer X Word Counter - Text Analysis Tool Logo"
95
+ title="Humanizer X Word Counter - Count words, characters, and analyze text"
96
+ className="mx-auto w-40 h-30 object-contain"
97
+ />
98
+ <h1 className="text-5xl md:text-6xl font-bold leading-tight mt-6">
99
+ <span className="bg-gradient-to-r from-indigo-500 to-indigo-700 bg-clip-text text-transparent">
100
+ Word
101
+ </span>{' '}
102
+ Counter
103
+ </h1>
104
+ <p className="text-xl md:text-2xl mt-7 mb-7 text-blue-100">
105
+ Count words, characters, sentences, and estimated reading time. Supports file uploads and quick copy features.
106
+ </p>
107
+ <div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
108
+ <div className="bg-white/10 backdrop-blur-sm rounded-lg px-6 py-3">
109
+ <span className="text-lg font-semibold">Current Tool: </span>
110
+ <span className="text-blue-200 capitalize">Word Counter</span>
111
+ </div>
112
+ <Link
113
+ to="/"
114
+ className="inline-flex items-center gap-2 bg-white text-purple-600 px-6 py-3 rounded-lg font-semibold hover:bg-gray-100 transition-colors"
115
+ >
116
+ <Shield size={20} />
117
+ Humanizer X
118
+ <ChevronRight size={16} />
119
+ </Link>
120
+ </div>
121
+ </div>
122
+ </section>
123
+
124
+ {/* Input Section */}
125
+ <div className="container mx-auto px-4 mt-10">
126
+ <div className={`${cardBg} rounded-xl shadow-lg p-6 border ${borderCard} space-y-4`}>
127
+ {/* Upload */}
128
+ <div className="flex justify-between items-center mb-2">
129
+ <h3 className={`text-sm font-medium ${secondaryText}`}>Input Text</h3>
130
+ <label className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white text-sm rounded-lg cursor-pointer hover:bg-blue-700 transition-colors shadow-md">
131
+ <Upload size={18} />
132
+ <span>Upload</span>
133
+ <input type="file" accept=".txt,.docx" onChange={handleFileUpload} className="hidden" ref={fileInputRef} />
134
+ </label>
135
+ </div>
136
+
137
+ {/* Textarea */}
138
+ <div
139
+ className={`relative h-96 border rounded-xl shadow-inner transition-all duration-200 ${
140
+ theme === 'dark'
141
+ ? 'bg-gradient-to-br from-gray-800 to-gray-900 border-gray-700 text-white'
142
+ : 'bg-gradient-to-br from-gray-50 to-white border-gray-300 text-gray-900'
143
+ }`}
144
+ >
145
+ <textarea
146
+ value={text}
147
+ onChange={handleTextChange}
148
+ placeholder="Paste text here or upload a file..."
149
+ className="w-full h-full p-4 resize-none bg-transparent focus:ring-2 focus:ring-blue-500 focus:border-transparent rounded-xl"
150
+ style={{ lineHeight: '1.6', fontSize: '0.95rem', fontFamily: 'Inter, sans-serif' }}
151
+ />
152
+ {uploadedFileName && (
153
+ <p className={`absolute bottom-2 left-4 text-xs ${secondaryText} truncate max-w-[70%]`}>
154
+ Uploaded: {uploadedFileName}
155
+ </p>
156
+ )}
157
+ </div>
158
+
159
+ {/* Metrics + Copy */}
160
+ <div className="flex justify-between items-center">
161
+ <div className={`text-sm ${secondaryText}`}>
162
+ Words: {wordCount.toLocaleString()} | Characters: {charCount.toLocaleString()}
163
+ </div>
164
+ <div className="relative">
165
+ <button
166
+ onClick={handleCopy}
167
+ disabled={!text}
168
+ className="flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
169
+ >
170
+ <Copy size={16} /> Copy
171
+ </button>
172
+ {copied && (
173
+ <span className="absolute -top-6 right-0 animate-fade text-xs bg-white text-green-600 px-2 py-1 rounded shadow">
174
+ Copied!
175
+ </span>
176
+ )}
177
+ </div>
178
+ </div>
179
+
180
+ {/* Error */}
181
+ {error && (
182
+ <div
183
+ className={`p-3 border rounded-lg ${
184
+ theme === 'dark' ? 'bg-red-900/20 border-red-800 text-red-300' : 'bg-red-50 border-red-200 text-red-700'
185
+ }`}
186
+ >
187
+ <AlertCircle size={16} className="inline mr-2" />
188
+ {error}
189
+ </div>
190
+ )}
191
+ </div>
192
+ </div>
193
+
194
+ {/* Feature Cards */}
195
+ <WordCounterFeatures theme={theme} text={text} />
196
+
197
+ {/* --- Humanizer X Word Counter Full Article Section --- */}
198
+ <section className="py-16 mb-16">
199
+ <div className="container mx-auto px-4">
200
+ <div className={`${theme === 'dark' ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'} rounded-xl shadow-lg border p-10 space-y-8 transform scale-100 transition-transform duration-300`}>
201
+
202
+ <h2 className={`text-5xl md:text-5xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
203
+ Humanizer X Word Counter – Precision in Every Word
204
+ </h2>
205
+
206
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
207
+ Introduction:
208
+ </h3>
209
+ <p className={`text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
210
+ Whether you’re a student, blogger, or professional writer, knowing the length of your text is essential. The <a href="https://www.humanizerx.pro" className="text-blue-500 underline" target="_blank" rel="noopener noreferrer">Humanizer X</a> Word Counter makes it simple to track words, characters, sentences, and even reading time. No more manual counting—our tool saves you time and ensures your content meets academic, professional, or SEO requirements with ease.
211
+ </p>
212
+
213
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
214
+ Why Choose Humanizer X Word Counter?
215
+ </h3>
216
+ <p className={`text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
217
+ Precision matters in writing. With instant metrics, you can maintain clarity, structure, and SEO performance. Whether you’re crafting an essay, article, or social media post, this counter helps you stay on track and deliver professional, human-like content every time.
218
+ </p>
219
+
220
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
221
+ Key Features:
222
+ </h3>
223
+ <ul className={`list-disc list-inside space-y-2 text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
224
+ <li><strong>Word Count:</strong> Instantly see your word totals to match assignment, article, or SEO limits.</li>
225
+ <li><strong>Character Count:</strong> Count characters with or without spaces—perfect for tweets, ads, and metadata.</li>
226
+ <li><strong>Sentence Count:</strong> Review sentence numbers to balance flow and readability.</li>
227
+ <li><strong>Reading Time:</strong> Estimate how long it takes to read your content and keep audiences engaged.</li>
228
+ <li><strong>File Upload:</strong> Upload .txt or .docx files to analyze large documents quickly.</li>
229
+ <li><strong>Copy Function:</strong> One-click copy for editing, publishing, or sharing anywhere.</li>
230
+ </ul>
231
+
232
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
233
+ Seamless Humanizer X Integration:
234
+ </h3>
235
+ <p className={`text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
236
+ Beyond counting, the Word Counter connects with <a href="https://www.humanizerx.pro" className="text-blue-500 underline" target="_blank" rel="noopener noreferrer">Humanizer X</a> tools. Refine your text with our AI humanizer, GPT checker, and AI text converter to ensure not only the right length but also natural, polished, and authentic writing.
237
+ </p>
238
+
239
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
240
+ Benefits You’ll Gain:
241
+ </h3>
242
+ <ul className={`list-disc list-inside space-y-2 text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
243
+ <li>Stay within word and character limits for school, business, or SEO tasks.</li>
244
+ <li>Boost readability by monitoring sentence structure and flow.</li>
245
+ <li>Save time with instant feedback—no need for manual counts.</li>
246
+ <li>Enhance SEO by keeping keyword density balanced and effective.</li>
247
+ <li>Combine with Humanizer X tools for fully humanized, professional content.</li>
248
+ </ul>
249
+
250
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
251
+ Perfect for Every Writer:
252
+ </h3>
253
+ <p className={`text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
254
+ From essays and reports to blogs and business documents, this tool adapts to your needs. Students meet length requirements, bloggers keep posts engaging, and businesses maintain professional standards with ease.
255
+ </p>
256
+
257
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
258
+ SEO and Readability Boost:
259
+ </h3>
260
+ <p className={`text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
261
+ Well-structured content isn’t just reader-friendly—it’s also search engine–friendly. By balancing word counts and sentence lengths, the Word Counter helps you create content that ranks well and keeps audiences hooked.
262
+ </p>
263
+
264
+ <h3 className={`text-2xl md:text-4xl font-bold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>
265
+ Conclusion:
266
+ </h3>
267
+ <p className={`text-lg md:text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-700'}`}>
268
+ The <a href="https://www.humanizerx.pro" className="text-blue-500 underline" target="_blank" rel="noopener noreferrer">Humanizer X</a> Word Counter is more than just a counter—it’s a complete writing assistant. Track every word, character, and sentence while refining your text into professional, human-like content. Start optimizing your writing today with Humanizer X.
269
+ </p>
270
+
271
+ </div>
272
+ </div>
273
+ </section>
274
+
275
+
276
+ {/* CTA */}
277
+ <section className="py-16 bg-gradient-to-r from-indigo-900 to-slate-900 text-white">
278
+ <div className="container mx-auto px-4 text-center">
279
+ <h2 className="text-3xl font-bold mb-4">Let Humanizer X Perfect Your Content</h2>
280
+ <p className="text-xl mb-8 text-blue-100">
281
+ Humanizer X refines AI text into authentic, human-like writing with built-in originality checks.
282
+ </p>
283
+ <Link
284
+ to="/"
285
+ className="inline-flex items-center gap-2 bg-white text-indigo-600 px-8 py-4 rounded-lg font-semibold text-lg hover:bg-gray-100 transition-colors"
286
+ >
287
+ <Shield size={24} />
288
+ Try Humanizer X
289
+ <ChevronRight size={20} />
290
+ </Link>
291
+ </div>
292
+ </section>
293
+ <Footer />
294
+ </div>
295
+ );
296
+ };
297
+
298
+ export default WordCounterPage;
299
+
300
+ // ---------------- Feature Cards ----------------
301
+ const WordCounterFeatures = ({ theme, text }: any) => {
302
+ const wordCount = text.trim().split(/\s+/).filter((w: string) => w.length > 0).length;
303
+ const charCount = text.length;
304
+ const sentenceCount = text.split(/[.!?]+/).filter(s => s.trim().length > 0).length;
305
+ const readingTime = Math.ceil(wordCount / 200);
306
+
307
+ return (
308
+ <section className="mt-10 py-16">
309
+ <div className="container mx-auto px-4">
310
+ <div className={`${theme === 'dark' ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'} rounded-xl shadow-lg border p-8`}>
311
+ <div className="text-center mb-12">
312
+ <h2 className={`text-3xl font-bold mb-4 ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>Word Counter Output</h2>
313
+ </div>
314
+ <div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
315
+ <FeatureCard title="Words" value={wordCount.toLocaleString()} bgColor="blue" theme={theme} />
316
+ <FeatureCard title="Characters" value={charCount.toLocaleString()} bgColor="purple" theme={theme} />
317
+ <FeatureCard title="Sentences" value={sentenceCount} bgColor="green" theme={theme} />
318
+ <FeatureCard title="Reading Time" value={`${readingTime} min`} bgColor="orange" theme={theme} />
319
+ </div>
320
+ </div>
321
+ </div>
322
+ </section>
323
+ );
324
+ };
325
+
326
+ const FeatureCard = ({ title, value, bgColor, theme }: any) => {
327
+ const colors: any = {
328
+ blue: ['bg-blue-100', 'dark:bg-blue-900/30', 'text-blue-600', 'dark:text-blue-400'],
329
+ purple: ['bg-purple-100', 'dark:bg-purple-900/30', 'text-purple-600', 'dark:text-purple-400'],
330
+ green: ['bg-green-100', 'dark:bg-green-900/30', 'text-green-600', 'dark:text-green-400'],
331
+ orange: ['bg-orange-100', 'dark:bg-orange-900/30', 'text-orange-600', 'dark:text-orange-400'],
332
+ };
333
+ const [bgLight, bgDark, textLight, textDark] = colors[bgColor];
334
+
335
+ return (
336
+ <div className={`${theme === 'dark' ? 'bg-gray-700' : 'bg-gray-50'} p-6 rounded-xl text-center flex flex-col items-center justify-center`}>
337
+ <div className={`w-20 h-20 flex items-center justify-center mb-4 rounded-full ${theme === 'dark' ? bgDark : bgLight}`}>
338
+ <span className={`font-bold text-2xl ${theme === 'dark' ? textDark : textLight}`}>{value}</span>
339
+ </div>
340
+ <h3 className={`text-lg font-semibold ${theme === 'dark' ? 'text-white' : 'text-gray-900'}`}>{title}</h3>
341
+ </div>
342
+ );
343
+ };
src/synonymsData.js ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const synonymsData = {
2
+ "happy": [
3
+ "happy",
4
+ "well-chosen"
5
+ ],
6
+ "sad": [
7
+ "sad",
8
+ "deplorable"
9
+ ],
10
+ "angry": [
11
+ "angry",
12
+ "furious",
13
+ "raging",
14
+ "tempestuous",
15
+ "wild"
16
+ ],
17
+ "good": [
18
+ "good",
19
+ "right",
20
+ "ripe",
21
+ "well,",
22
+ "unspoiled",
23
+ "unspoilt"
24
+ ],
25
+ "bad": [
26
+ "bad",
27
+ "defective",
28
+ "forged",
29
+ "unfit",
30
+ "unsound",
31
+ "risky"
32
+ ],
33
+ "big": [
34
+ "big",
35
+ "bighearted",
36
+ "bounteous",
37
+ "bountiful",
38
+ "freehanded",
39
+ "handsome"
40
+ ],
41
+ "small": [
42
+ "small",
43
+ "little",
44
+ "small",
45
+ "s"
46
+ ],
47
+ "fast": [
48
+ "fast",
49
+ "firm",
50
+ "immobile",
51
+ "fast",
52
+ "tight",
53
+ "fast"
54
+ ],
55
+ "slow": [
56
+ "slow",
57
+ "slack",
58
+ "slacken",
59
+ "slow",
60
+ "r",
61
+ "slow"
62
+ ],
63
+ "strong": [
64
+ "strong",
65
+ "warm",
66
+ "strong",
67
+ "a"
68
+ ],
69
+ "weak": [
70
+ "weak",
71
+ "faint",
72
+ "unaccented",
73
+ "decrepit",
74
+ "fallible",
75
+ "watery"
76
+ ],
77
+ "hot": [
78
+ "hot",
79
+ "live",
80
+ "red-hot",
81
+ "spicy",
82
+ "raging"
83
+ ],
84
+ "cold": [
85
+ "cold",
86
+ "cold-blooded",
87
+ "inhuman",
88
+ "insensate",
89
+ "frigid",
90
+ "stale"
91
+ ],
92
+ "rich": [
93
+ "rich",
94
+ "deep",
95
+ "fat",
96
+ "ample",
97
+ "full-bodied"
98
+ ],
99
+ "poor": [
100
+ "poor",
101
+ "hapless",
102
+ "inadequate"
103
+ ],
104
+ "new": [
105
+ "new",
106
+ "young",
107
+ "unexampled",
108
+ "new",
109
+ "r"
110
+ ],
111
+ "old": [
112
+ "old",
113
+ "older"
114
+ ],
115
+ "beautiful": [],
116
+ "ugly": [
117
+ "ugly",
118
+ "atrocious",
119
+ "despicable",
120
+ "surly"
121
+ ],
122
+ "smart": [
123
+ "smart",
124
+ "smarting",
125
+ "smartness",
126
+ "smart",
127
+ "s",
128
+ "smart"
129
+ ],
130
+ "dumb": [
131
+ "dumb",
132
+ "mute",
133
+ "silent"
134
+ ],
135
+ "easy": [
136
+ "easy",
137
+ "light",
138
+ "loose",
139
+ "promiscuous",
140
+ "sluttish",
141
+ "wanton"
142
+ ],
143
+ "difficult": [
144
+ "difficult",
145
+ "hard"
146
+ ],
147
+ "interesting": [],
148
+ "boring": [
149
+ "boring",
150
+ "deadening",
151
+ "dull",
152
+ "ho-hum",
153
+ "irksome",
154
+ "slow"
155
+ ],
156
+ "funny": [
157
+ "funny",
158
+ "amusing",
159
+ "curious",
160
+ "fishy"
161
+ ],
162
+ "serious": [
163
+ "serious",
164
+ "dangerous",
165
+ "good",
166
+ "serious",
167
+ "unplayful"
168
+ ],
169
+ "handsome": [
170
+ "handsome",
171
+ "big",
172
+ "fine-looking"
173
+ ],
174
+ "brave": [
175
+ "brave",
176
+ "braw",
177
+ "gay",
178
+ "brave",
179
+ "courageous",
180
+ "brave"
181
+ ],
182
+ "cowardly": [
183
+ "cowardly",
184
+ "fearful"
185
+ ],
186
+ "honest": [
187
+ "honest",
188
+ "fair",
189
+ "honest",
190
+ "honorable"
191
+ ],
192
+ "dishonest": [
193
+ "dishonest",
194
+ "dishonorable",
195
+ "dishonest",
196
+ "s"
197
+ ]
198
+ };
src/types/user.ts ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface UserStats {
2
+ textsHumanized: number; // Number of texts humanized
3
+ wordsProcessed: number; // Total words processed
4
+ gptAverage: number; // GPT score or percentage
5
+ totalSessions: number; // Total sessions the user has used
6
+ totalDuration: number;
7
+ averageSessionTime: number; // Average session time in minutes
8
+ }
9
+
10
+ export interface UserPreferences {
11
+ theme: 'light' | 'dark' | 'system'; // Theme preference
12
+ language: string; // Language code, e.g., 'en'
13
+ timezone: string; // Timezone, e.g., 'UTC'
14
+ emailNotifications: boolean; // Receive email notifications
15
+ pushNotifications: boolean; // Receive push notifications
16
+ marketingEmails: boolean; // Marketing emails subscription
17
+ twoFactorEnabled: boolean; // 2FA enabled
18
+ profileVisibility: 'public' | 'private' | 'friends'; // Profile visibility
19
+ }
20
+
21
+ export interface UserProfile {
22
+ id: string; // UID of the user
23
+ name: string; // Full name
24
+ nickname: string; // Nickname
25
+ username: string; // Username handle
26
+ usernameChanged?: boolean; // Whether username was changed
27
+ bio: string; // User bio
28
+ email: string; // Email address
29
+ avatar: string; // Avatar image URL
30
+ joinDate: string; // Join date
31
+ lastActive: string; // Last active time
32
+ stats: UserStats; // Nested stats object
33
+ preferences: UserPreferences; // Nested preferences object
34
+ }
35
+
36
+ export interface UserActivity {
37
+ id: string; // Activity document ID
38
+ action: string; // Action description
39
+ timestamp: string; // Activity timestamp
40
+ details?: string; // Optional extra details
41
+ type: 'login' | 'profile' | 'settings' | 'security' | 'data'; // Activity type
42
+ }
src/vite-env.d.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ /// <reference types="vite/client" />