Skip to content

Commit 9de01b3

Browse files
authored
Merge pull request #11 from MYSTICxSAM/profile
1.Added reset option at login page 2.Added a profile page which works on clicking profile page"
2 parents 57502bc + b8f93f4 commit 9de01b3

9 files changed

Lines changed: 537 additions & 3 deletions

File tree

src/App.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@
4040
.read-the-docs {
4141
color: #888;
4242
}
43+

src/App.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import Blogs from "./pages/Blogs"
1515
import AboutUs from "./pages/AboutUs";
1616
import ContactUs from "./pages/ContactUs";
1717
import ScrollToTop from "./components/ScrollToTop";
18+
import Profile from "./pages/Profile";
19+
import ForgotPassword from "./pages/auth/Forgot";
1820

1921
// Import i18n configuration
2022
import './i18n';
@@ -32,9 +34,12 @@ const AppRoutes = () => {
3234
<Route path="/blog" element={<Blogs user_role={userRole} />} />
3335
<Route path="/about" element={<AboutUs />} />
3436
<Route path="/contact" element={<ContactUs />} />
37+
<Route path="/profile" element={<Profile />} />
3538
<Route path="/auth" element={<AuthLayout />}>
3639
<Route path="login" element={<Login />} />
3740
<Route path="register" element={<Register />} />
41+
<Route path="Forgot" element={<ForgotPassword />} />
42+
3843
</Route>
3944
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
4045
<Route path="*" element={<NotFound />} />

src/components/Footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ const Footer: React.FC = () => {
117117
&copy; {new Date().getFullYear()} GoBuild. All rights reserved.
118118
</p>
119119
<div className="flex space-x-6">
120-
<Link to="/" className="text-gray-400 hover:text-white text-sm">Privacy Policy</Link>
120+
<Link to="/policy" className="text-gray-400 hover:text-white text-sm">Privacy Policy</Link>
121121
<Link to="/" className="text-gray-400 hover:text-white text-sm">Terms of Service</Link>
122122
<Link to="/" className="text-gray-400 hover:text-white text-sm">Cookies</Link>
123123
</div>

src/components/UserProfileMenu.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import { Button } from '@/components/ui/button';
33
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
4+
import { useNavigate } from 'react-router-dom';
45
import {
56
DropdownMenu,
67
DropdownMenuContent,
@@ -16,7 +17,7 @@ import { useTranslation } from 'react-i18next';
1617
const UserProfileMenu = () => {
1718
const { user, signOut } = useAuth();
1819
const { t } = useTranslation();
19-
20+
const navigate = useNavigate();
2021
if (!user) {
2122
return null;
2223
}
@@ -42,7 +43,7 @@ const UserProfileMenu = () => {
4243
{user.user_metadata.full_name || user.email}
4344
</DropdownMenuLabel>
4445
<DropdownMenuSeparator />
45-
<DropdownMenuItem className="cursor-pointer">
46+
<DropdownMenuItem className="cursor-pointer" onClick={() => navigate('/profile')}>
4647
<User className="mr-2 h-4 w-4" />
4748
<span>{t('common.profile')}</span>
4849
</DropdownMenuItem>

src/index.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,33 @@
246246
.animate-pulse-line-reverse {
247247
animation: pulseBackward 3s ease-in-out infinite;
248248
}
249+
@keyframes slideInFromRight {
250+
0% {
251+
transform: translateX(100%);
252+
opacity: 0;
253+
}
254+
100% {
255+
transform: translateX(0);
256+
opacity: 1;
257+
}
258+
}
259+
260+
@keyframes slideOutToRight {
261+
0% {
262+
transform: translateX(0);
263+
opacity: 1;
264+
}
265+
100% {
266+
transform: translateX(100%);
267+
opacity: 0;
268+
}
269+
}
270+
271+
.animate-slideInFromRight {
272+
animation: slideInFromRight 0.3s ease-out forwards;
273+
}
274+
275+
.animate-slideOutToRight {
276+
animation: slideOutToRight 0.3s ease-in forwards;
277+
}
249278
}

src/pages/Policy.tsx

Lines changed: 172 additions & 0 deletions
Large diffs are not rendered by default.

src/pages/Profile.tsx

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { useNavigate, Link } from 'react-router-dom';
3+
import { useAuth } from '@/contexts/AuthContext';
4+
import Navbar from '@/components/Navbar';
5+
6+
const accentColor = 'text-blue-600';
7+
8+
const Profile: React.FC = () => {
9+
10+
const { user } = useAuth();
11+
const [tab, setTab] = useState<'active' | 'history'>('active');
12+
const [showDeletePopup, setShowDeletePopup] = useState(false);
13+
const [popupAnimatingOut, setPopupAnimatingOut] = useState(false);
14+
const [deleteReason, setDeleteReason] = useState('');
15+
const [email, setEmail] = useState('');
16+
const [password, setPassword] = useState('');
17+
const [deleting, setDeleting] = useState(false);
18+
19+
const navigate = useNavigate();
20+
21+
const activeRequests = [{name:"Mason",status:"Doing",id:1234},{name:"Plumber",status:"Doing",id:1234}]; //is format me data dalna hai
22+
const serviceHistory = [{name:"Mason",status:"Done",id:1234}]; //is format me data dalna hai
23+
const displayName = user?.user_metadata?.full_name || user?.email || 'User';
24+
25+
const openDeletePopup = () => {
26+
setDeleteReason('');
27+
setEmail('');
28+
setPassword('');
29+
setShowDeletePopup(true);
30+
};
31+
useEffect(()=>{
32+
if(!user){
33+
navigate("/auth/login")
34+
}
35+
})
36+
const closeDeletePopup = () => {
37+
setPopupAnimatingOut(true);
38+
setTimeout(() => {
39+
setPopupAnimatingOut(false);
40+
setShowDeletePopup(false);
41+
}, 300); // duration matches CSS animation of 0.3s
42+
};
43+
44+
const canDelete = deleteReason.trim() !== '' && email.trim() !== '' && password.trim() !== '';
45+
46+
const handleDeleteAccount = () => {
47+
if (!canDelete) return;
48+
49+
if (window.confirm('Are you sure you want to delete your account? This action cannot be undone.')) {
50+
setDeleting(true);
51+
// TODO: Implement backend call for actual delete with re-authentication and reason
52+
setTimeout(() => {
53+
setDeleting(false);
54+
setShowDeletePopup(false);
55+
alert('Account deleted successfully.');
56+
}, 2000);
57+
}
58+
};
59+
60+
return (
61+
<div className="bg-white min-h-screen py-8 px-4 sm:px-6 md:px-16 font-sans relative">
62+
<Navbar />
63+
<div className="max-w-5xl mx-auto w-full mt-14">
64+
{/* Header */}
65+
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6 mb-12 px-4 md:px-0">
66+
<div>
67+
<h1 className="text-3xl sm:text-4xl font-extrabold mb-3">
68+
<span className={accentColor}>{displayName}</span>
69+
<span className="text-gray-900">’s Dashboard</span>
70+
</h1>
71+
<p className="text-gray-500 text-base sm:text-lg font-medium max-w-xl">
72+
Manage your <span className={accentColor}>service requests</span> and track their progress.
73+
</p>
74+
</div>
75+
<button
76+
className="bg-blue-600 text-white px-4 py-3 rounded-lg font-bold flex items-center justify-center gap-3 hover:bg-blue-700 transition text-base sm:text-lg shadow-lg min-w-[180px]"
77+
onClick={() => navigate('/services')}
78+
>
79+
<span className="text-3xl font-extrabold leading-none">+</span> New Service Request
80+
</button>
81+
</div>
82+
83+
{/* Tabs */}
84+
<div className="flex bg-gray-100 rounded-lg overflow-hidden shadow mb-10 text-base md:text-lg font-semibold border border-gray-300">
85+
<button
86+
className={`flex-1 py-4 transition duration-200 ${tab === 'active' ? `bg-white ${accentColor} border-b-6 border-blue-600 shadow-inner` : 'text-gray-400 hover:text-blue-600'}`}
87+
onClick={() => setTab('active')}
88+
aria-selected={tab === 'active'}
89+
role="tab"
90+
>
91+
Active Requests ({activeRequests.length})
92+
</button>
93+
<button
94+
className={`flex-1 py-4 transition duration-200 ${tab === 'history' ? `bg-white ${accentColor} border-b-6 border-blue-600 shadow-inner` : 'text-gray-400 hover:text-blue-600'}`}
95+
onClick={() => setTab('history')}
96+
aria-selected={tab === 'history'}
97+
role="tab"
98+
>
99+
Service History ({serviceHistory.length})
100+
</button>
101+
</div>
102+
103+
{/* Content */}
104+
<div className="bg-white rounded-xl shadow-xl p-6 md:p-8 min-h-[260px]">
105+
{tab === 'active' ? (
106+
activeRequests.length > 0 ? (
107+
activeRequests.map((req) => (
108+
<div key={req.id} className="border-b last:border-none py-4 flex flex-col md:flex-row justify-between md:items-center">
109+
<div>
110+
<h3 className="font-semibold text-xl md:text-2xl">{req.name}</h3>
111+
<p className="text-gray-600 mt-2 md:text-lg">
112+
Status: <span className="text-blue-700 font-semibold">{req.status}</span>
113+
</p>
114+
</div>
115+
<button className="mt-5 md:mt-0 bg-gray-100 hover:bg-gray-200 text-blue-700 font-semibold px-6 py-3 rounded-lg transition text-lg whitespace-nowrap">
116+
Track Progress
117+
</button>
118+
</div>
119+
))
120+
) : (
121+
<div className="flex flex-col items-center justify-center h-48 md:h-64 text-center text-blue-100">
122+
<div className="text-3xl font-bold mb-2 text-gray-700">No Active Requests</div>
123+
<div className="text-gray-500 text-xl">You haven't requested any services yet.</div>
124+
</div>
125+
)
126+
) : serviceHistory.length > 0 ? (
127+
serviceHistory.map((req) => (
128+
<div key={req.id} className="border-b last:border-none py-6">
129+
<div className="font-semibold text-2xl">{req.name}</div>
130+
<div className="text-gray-600 mt-1 text-lg">
131+
Status: <span className="text-green-700 font-semibold">{req.status}</span>
132+
</div>
133+
</div>
134+
))
135+
) : (
136+
<div className="flex flex-col items-center justify-center h-48 md:h-64 text-center text-blue-100">
137+
<div className="text-3xl font-bold mb-2 text-gray-700">No Service History</div>
138+
<div className="text-gray-500 text-xl">You haven't completed any services yet.</div>
139+
</div>
140+
)}
141+
</div>
142+
143+
{/* Delete Account */}
144+
<div className="mt-14 flex justify-center px-4 md:px-0">
145+
<button
146+
onClick={openDeletePopup}
147+
className="bg-red-600 text-white px-8 py-4 rounded-lg font-bold hover:bg-red-700 transition shadow-lg max-w-md w-full"
148+
>
149+
Delete My Account
150+
</button>
151+
</div>
152+
153+
{/* Delete Reason Popup */}
154+
{showDeletePopup && (
155+
<div
156+
className={`fixed top-0 right-0 z-50 w-full sm:w-96 h-full bg-white shadow-xl p-6 flex flex-col
157+
${popupAnimatingOut ? 'animate-slideOutToRight' : 'animate-slideInFromRight'}`}
158+
style={{ animationDuration: '0.3s', animationFillMode: 'forwards' }}
159+
>
160+
<div className="flex justify-between items-center mb-6">
161+
<h2 className="text-xl font-bold text-gray-800">Why are you deleting your account?</h2>
162+
<button
163+
onClick={closeDeletePopup}
164+
className="text-gray-600 hover:text-gray-900 text-2xl font-bold leading-none"
165+
aria-label="Close popup"
166+
>
167+
&times;
168+
</button>
169+
</div>
170+
171+
<textarea
172+
className="border border-gray-300 rounded p-3 resize-none w-full h-24 focus:outline-blue-500 mb-4"
173+
placeholder="Please provide a brief reason..."
174+
value={deleteReason}
175+
onChange={(e) => setDeleteReason(e.target.value)}
176+
/>
177+
178+
<input
179+
type="email"
180+
className="border border-gray-300 rounded p-3 mb-4 w-full focus:outline-blue-500"
181+
placeholder="Enter your email"
182+
value={email}
183+
onChange={(e) => setEmail(e.target.value)}
184+
aria-label="Email"
185+
/>
186+
187+
<div className="flex items-center mb-6">
188+
<input
189+
type="password"
190+
className="border border-gray-300 rounded p-3 w-full focus:outline-blue-500"
191+
placeholder="Enter your password"
192+
value={password}
193+
onChange={(e) => setPassword(e.target.value)}
194+
aria-label="Password"
195+
/>
196+
<Link
197+
to="pages/auth/ForgotPassword.tsx"
198+
className="text-blue-600 ml-3 whitespace-nowrap hover:underline"
199+
>
200+
Forgot?
201+
</Link>
202+
</div>
203+
204+
<button
205+
onClick={handleDeleteAccount}
206+
disabled={!(deleteReason.trim() && email.trim() && password.trim()) || deleting}
207+
className={`mt-auto py-3 rounded-lg font-bold text-white w-full transition ${
208+
!(deleteReason.trim() && email.trim() && password.trim()) || deleting
209+
? 'bg-gray-300 cursor-not-allowed'
210+
: 'bg-red-600 hover:bg-red-700 cursor-pointer'
211+
}`}
212+
>
213+
{deleting ? 'Deleting Account...' : 'Delete Account'}
214+
</button>
215+
</div>
216+
)}
217+
</div>
218+
</div>
219+
);
220+
};
221+
222+
export default Profile;

0 commit comments

Comments
 (0)