From 1b22557cdbc2ab260ad3797b6bcf39d1bbf7812e Mon Sep 17 00:00:00 2001 From: Vivek W <92390419+AryanVBW@users.noreply.github.com> Date: Mon, 6 Jan 2025 22:26:28 +0530 Subject: [PATCH 1/2] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ce209b..3438f07 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This is the deployment repository for the Dev Club Portal, optimized for Vercel deployment. For contributing to the project, please visit our [organization repository](https://github.com/nst-sdc/DevCoin). ### 🔗 Important Links -- 🌐 [Live Demo](https://portal-aryanvbw.vercel.app/) +- 🌐 [Live Demo](https://portal-cyan-alpha.vercel.app/) - 🏢 [Organization Repo](https://github.com/nst-sdc/DevCoin) - 📚 [Documentation](./PROJECT_DOCUMENTATION.md) - 📋 [Latest Release Notes](./RELEASE_v1.1.0.md) From a4a91ab7526e6739626b32e206c5af8ff8105925 Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 6 Jan 2025 23:18:50 +0530 Subject: [PATCH 2/2] feat: add change password functionality --- package-lock.json | 7 +- src/components/ChangePasswordModal.tsx | 145 +++++++++++++++++++++++++ src/pages/ProfilePage.tsx | 35 ++++-- src/services/auth.ts | 48 ++++++++ 4 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 src/components/ChangePasswordModal.tsx diff --git a/package-lock.json b/package-lock.json index 47b9906..118b287 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3919,9 +3919,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -3929,6 +3929,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, diff --git a/src/components/ChangePasswordModal.tsx b/src/components/ChangePasswordModal.tsx new file mode 100644 index 0000000..efa800f --- /dev/null +++ b/src/components/ChangePasswordModal.tsx @@ -0,0 +1,145 @@ +import React, { useState } from 'react'; +import { X, Lock } from 'lucide-react'; +import { toast } from 'react-toastify'; +import { changePassword } from '../services/auth'; +import { User } from '../types/auth'; + +interface ChangePasswordModalProps { + user: User; + isOpen: boolean; + onClose: () => void; +} + +export default function ChangePasswordModal({ user, isOpen, onClose }: ChangePasswordModalProps) { + const [formData, setFormData] = useState({ + currentPassword: '', + newPassword: '', + confirmPassword: '', + }); + const [loading, setLoading] = useState(false); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (formData.newPassword !== formData.confirmPassword) { + toast.error('New passwords do not match'); + return; + } + + setLoading(true); + try { + await changePassword(user.id, formData.currentPassword, formData.newPassword); + toast.success('Password changed successfully'); + onClose(); + // Clear form data + setFormData({ + currentPassword: '', + newPassword: '', + confirmPassword: '', + }); + } catch (error: any) { + toast.error(error.message); + } finally { + setLoading(false); + } + }; + + if (!isOpen) return null; + + return ( +
+
+ + +
+ +

Change Password

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ Password must: +
    +
  • Be at least 8 characters long
  • +
  • Contain at least one uppercase letter
  • +
  • Contain at least one number
  • +
  • Contain at least one special character (!@#$%^&*)
  • +
+
+ +
+ + +
+
+
+
+ ); +} diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 36af6e5..e43cfb2 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -1,13 +1,15 @@ import React, { useState } from 'react'; import { useAuth } from '../contexts/AuthContext'; -import { Github, Linkedin, Mail, Award, Calendar, Edit2 } from 'lucide-react'; +import { Github, Linkedin, Mail, Award, Calendar, Edit2, Lock } from 'lucide-react'; import EditProfileModal from '../components/EditProfileModal'; +import ChangePasswordModal from '../components/ChangePasswordModal'; import { updateUserProfile } from '../services/localStore'; import type { User } from '../types/auth'; export default function ProfilePage() { const { user } = useAuth(); const [isEditModalOpen, setIsEditModalOpen] = useState(false); + const [isChangePasswordModalOpen, setIsChangePasswordModalOpen] = useState(false); if (!user) { return ( @@ -26,14 +28,23 @@ export default function ProfilePage() { {/* Profile Header */}
- {/* Edit Profile Button */} - + {/* Profile Actions */} +
+ + +
{/* Profile picture overlapping the banner */} setIsEditModalOpen(false)} onUpdate={handleUpdateProfile} /> + + setIsChangePasswordModalOpen(false)} + />
); } diff --git a/src/services/auth.ts b/src/services/auth.ts index 39d1010..d87cbb8 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -45,4 +45,52 @@ export const signOut = async (): Promise => { export const getCurrentSession = (): User | null => { return getCurrentUser(); +}; + +export const changePassword = async (userId: string, currentPassword: string, newPassword: string): Promise => { + try { + const users = getUsers(); + const user = users.find(u => u.id === userId); + + if (!user) { + throw new Error('User not found'); + } + + // For demo purposes, we're only checking the super admin's password + if (user.email === 'vivek.aryanvbw@gmail.com' && currentPassword !== 'Vivek@2024') { + throw new Error('Current password is incorrect'); + } + + // In a real app, you would: + // 1. Verify the current password hash matches + // 2. Hash the new password + // 3. Update the password in the database + + // For this demo, we'll just validate the password format + if (newPassword.length < 8) { + throw new Error('Password must be at least 8 characters long'); + } + + if (!/[A-Z]/.test(newPassword)) { + throw new Error('Password must contain at least one uppercase letter'); + } + + if (!/[0-9]/.test(newPassword)) { + throw new Error('Password must contain at least one number'); + } + + if (!/[!@#$%^&*]/.test(newPassword)) { + throw new Error('Password must contain at least one special character (!@#$%^&*)'); + } + + // Update the password (in a real app, this would be hashed) + if (user.email === 'vivek.aryanvbw@gmail.com') { + // For demo purposes, we're only actually changing the super admin's password + user.password = newPassword; + const updatedUsers = users.map(u => u.id === userId ? user : u); + localStorage.setItem('users', JSON.stringify(updatedUsers)); + } + } catch (error: any) { + throw new Error(error.message); + } }; \ No newline at end of file