diff --git a/frontend/cmmty/pages/documents/[id]/settings/ChangePassword.tsx b/frontend/cmmty/pages/documents/[id]/settings/ChangePassword.tsx
new file mode 100644
index 0000000..47c57db
--- /dev/null
+++ b/frontend/cmmty/pages/documents/[id]/settings/ChangePassword.tsx
@@ -0,0 +1,39 @@
+import React, { useState } from 'react';
+
+const ChangePassword = () => {
+ const [oldPass, setOldPass] = useState('');
+ const [newPass, setNewPass] = useState('');
+
+ const handleChange = async () => {
+ try {
+ await fetch('/api/user/change-password', {
+ method: 'POST',
+ body: JSON.stringify({ oldPass, newPass }),
+ });
+ alert('Password changed successfully!');
+ } catch {
+ alert('Error changing password.');
+ }
+ };
+
+ return (
+
+
Change Password
+ setOldPass(e.target.value)}
+ />
+ setNewPass(e.target.value)}
+ />
+
+
+ );
+};
+
+export default ChangePassword;
diff --git a/frontend/cmmty/pages/documents/[id]/settings/NotificationToggles.tsx b/frontend/cmmty/pages/documents/[id]/settings/NotificationToggles.tsx
new file mode 100644
index 0000000..f0f6206
--- /dev/null
+++ b/frontend/cmmty/pages/documents/[id]/settings/NotificationToggles.tsx
@@ -0,0 +1,44 @@
+import React, { useState } from 'react';
+
+const NotificationToggles = () => {
+ const [prefs, setPrefs] = useState({
+ riskAlerts: true,
+ verificationEmails: true,
+ weeklyDigest: false,
+ });
+
+ const handleToggle = (key: keyof typeof prefs) => {
+ setPrefs({ ...prefs, [key]: !prefs[key] });
+ };
+
+ const handleSave = async () => {
+ try {
+ await fetch('/api/user/notifications', {
+ method: 'POST',
+ body: JSON.stringify(prefs),
+ });
+ alert('Notification preferences saved!');
+ } catch {
+ alert('Error saving preferences.');
+ }
+ };
+
+ return (
+
+
Notifications
+ {Object.keys(prefs).map((key) => (
+
+ ))}
+
+
+ );
+};
+
+export default NotificationToggles;
diff --git a/frontend/cmmty/pages/documents/[id]/settings/SettingsForm.tsx b/frontend/cmmty/pages/documents/[id]/settings/SettingsForm.tsx
new file mode 100644
index 0000000..033d004
--- /dev/null
+++ b/frontend/cmmty/pages/documents/[id]/settings/SettingsForm.tsx
@@ -0,0 +1,39 @@
+import React, { useState } from 'react';
+
+const SettingsForm = () => {
+ const [fullName, setFullName] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ const handleSave = async () => {
+ setLoading(true);
+ try {
+ // API call to save profile
+ await fetch('/api/user/update', {
+ method: 'POST',
+ body: JSON.stringify({ fullName }),
+ });
+ alert('Profile updated successfully!');
+ } catch (err) {
+ alert('Error updating profile.');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
Personal Information
+ setFullName(e.target.value)}
+ />
+
+
+ );
+};
+
+export default SettingsForm;
diff --git a/frontend/cmmty/pages/documents/[id]/settings/index.tsx b/frontend/cmmty/pages/documents/[id]/settings/index.tsx
new file mode 100644
index 0000000..ca46b7e
--- /dev/null
+++ b/frontend/cmmty/pages/documents/[id]/settings/index.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import SettingsForm from './SettingsForm';
+import NotificationToggles from './NotificationToggles';
+import ChangePassword from './ChangePassword';
+
+const SettingsPage = () => {
+ return (
+
+
User Profile Settings
+
+
+
+
+ );
+};
+
+export default SettingsPage;
diff --git a/frontend/cmmty/pages/forgot-password/ForgotPasswordForm.tsx b/frontend/cmmty/pages/forgot-password/ForgotPasswordForm.tsx
new file mode 100644
index 0000000..181729e
--- /dev/null
+++ b/frontend/cmmty/pages/forgot-password/ForgotPasswordForm.tsx
@@ -0,0 +1,41 @@
+import React, { useState } from 'react';
+
+const ForgotPasswordForm = () => {
+ const [email, setEmail] = useState('');
+ const [submitted, setSubmitted] = useState(false);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!email || !/\S+@\S+\.\S+/.test(email)) {
+ alert('Please enter a valid email.');
+ return;
+ }
+ try {
+ await fetch('/api/auth/forgot-password', {
+ method: 'POST',
+ body: JSON.stringify({ email }),
+ });
+ } catch (err) {
+ // swallow errors to avoid enumeration
+ } finally {
+ setSubmitted(true);
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default ForgotPasswordForm;
diff --git a/frontend/cmmty/pages/forgot-password/index.tsx b/frontend/cmmty/pages/forgot-password/index.tsx
new file mode 100644
index 0000000..a0f9965
--- /dev/null
+++ b/frontend/cmmty/pages/forgot-password/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import ForgotPasswordForm from './ForgotPasswordForm';
+
+const ForgotPasswordPage = () => {
+ return (
+
+ );
+};
+
+export default ForgotPasswordPage;
diff --git a/frontend/cmmty/pages/reset-password/ResetPasswordForm.tsx b/frontend/cmmty/pages/reset-password/ResetPasswordForm.tsx
new file mode 100644
index 0000000..4eef556
--- /dev/null
+++ b/frontend/cmmty/pages/reset-password/ResetPasswordForm.tsx
@@ -0,0 +1,65 @@
+import React, { useState } from 'react';
+import { useRouter } from 'next/router';
+
+interface Props {
+ token: string;
+}
+
+const ResetPasswordForm: React.FC = ({ token }) => {
+ const [newPass, setNewPass] = useState('');
+ const [confirmPass, setConfirmPass] = useState('');
+ const [error, setError] = useState('');
+ const [success, setSuccess] = useState(false);
+ const router = useRouter();
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (newPass.length < 8) {
+ setError('Password must be at least 8 characters.');
+ return;
+ }
+ if (newPass !== confirmPass) {
+ setError('Passwords do not match.');
+ return;
+ }
+ setError('');
+ try {
+ const res = await fetch('/api/auth/reset-password', {
+ method: 'POST',
+ body: JSON.stringify({ token, newPass }),
+ });
+ if (res.ok) {
+ setSuccess(true);
+ setTimeout(() => router.push('/login'), 3000);
+ } else {
+ setError('Invalid or expired token.');
+ }
+ } catch {
+ setError('Something went wrong. Please try again.');
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default ResetPasswordForm;
diff --git a/frontend/cmmty/pages/reset-password/index.tsx b/frontend/cmmty/pages/reset-password/index.tsx
new file mode 100644
index 0000000..949b019
--- /dev/null
+++ b/frontend/cmmty/pages/reset-password/index.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { useRouter } from 'next/router';
+import ResetPasswordForm from './ResetPasswordForm';
+
+const ResetPasswordPage = () => {
+ const router = useRouter();
+ const { token } = router.query;
+
+ return (
+
+
Reset Password
+ {token ? (
+
+ ) : (
+
Invalid or missing reset token.
+ )}
+
+ );
+};
+
+export default ResetPasswordPage;
diff --git a/frontend/cmmty/pages/verify-email/Confirmed.tsx b/frontend/cmmty/pages/verify-email/Confirmed.tsx
new file mode 100644
index 0000000..51a44ea
--- /dev/null
+++ b/frontend/cmmty/pages/verify-email/Confirmed.tsx
@@ -0,0 +1,41 @@
+import React, { useEffect, useState } from 'react';
+import { useRouter } from 'next/router';
+
+interface Props {
+ token: string;
+}
+
+const Confirmed: React.FC = ({ token }) => {
+ const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
+ const router = useRouter();
+
+ useEffect(() => {
+ const verifyEmail = async () => {
+ try {
+ const res = await fetch('/api/auth/verify-email', {
+ method: 'POST',
+ body: JSON.stringify({ token }),
+ });
+ if (res.ok) {
+ setStatus('success');
+ setTimeout(() => router.push('/dashboard'), 3000);
+ } else {
+ setStatus('error');
+ }
+ } catch {
+ setStatus('error');
+ }
+ };
+ verifyEmail();
+ }, [token, router]);
+
+ return (
+
+ {status === 'loading' &&
Verifying your email...
}
+ {status === 'success' &&
Email verified! Redirecting to dashboard...
}
+ {status === 'error' &&
Verification failed. Please try again.
}
+
+ );
+};
+
+export default Confirmed;
diff --git a/frontend/cmmty/pages/verify-email/Pending.tsx b/frontend/cmmty/pages/verify-email/Pending.tsx
new file mode 100644
index 0000000..23c432e
--- /dev/null
+++ b/frontend/cmmty/pages/verify-email/Pending.tsx
@@ -0,0 +1,29 @@
+import React, { useState } from 'react';
+
+const Pending = () => {
+ const [loading, setLoading] = useState(false);
+
+ const handleResend = async () => {
+ setLoading(true);
+ try {
+ await fetch('/api/auth/resend-verification', { method: 'POST' });
+ alert('Verification email resent!');
+ } catch {
+ alert('Error resending email.');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
Verify Your Email
+
Please check your inbox for a verification link.
+
+
+ );
+};
+
+export default Pending;
diff --git a/frontend/cmmty/pages/verify-email/index.tsx b/frontend/cmmty/pages/verify-email/index.tsx
new file mode 100644
index 0000000..aa999d6
--- /dev/null
+++ b/frontend/cmmty/pages/verify-email/index.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { useRouter } from 'next/router';
+import Pending from './Pending';
+import Confirmed from './Confirmed';
+
+const VerifyEmailPage = () => {
+ const router = useRouter();
+ const { token } = router.query;
+
+ if (token) {
+ return ;
+ }
+ return ;
+};
+
+export default VerifyEmailPage;
diff --git a/frontend/cmmty/styles/forgot-password.css b/frontend/cmmty/styles/forgot-password.css
new file mode 100644
index 0000000..d0cf60d
--- /dev/null
+++ b/frontend/cmmty/styles/forgot-password.css
@@ -0,0 +1,35 @@
+.forgot-container {
+ max-width: 400px;
+ margin: auto;
+ padding: 1rem;
+ text-align: center;
+}
+
+.forgot-form {
+ display: flex;
+ flex-direction: column;
+}
+
+.forgot-form input,
+.forgot-form button {
+ margin: 0.5rem 0;
+ padding: 0.75rem;
+ width: 100%;
+}
+
+.success-msg {
+ color: green;
+ margin-top: 1rem;
+}
+
+.back-link {
+ display: block;
+ margin-top: 1rem;
+ color: #0070f3;
+}
+
+@media (max-width: 600px) {
+ .forgot-container {
+ padding: 0.5rem;
+ }
+}
diff --git a/frontend/cmmty/styles/reset-password.css b/frontend/cmmty/styles/reset-password.css
new file mode 100644
index 0000000..e913435
--- /dev/null
+++ b/frontend/cmmty/styles/reset-password.css
@@ -0,0 +1,34 @@
+.reset-container {
+ max-width: 400px;
+ margin: auto;
+ padding: 1rem;
+ text-align: center;
+}
+
+.reset-form {
+ display: flex;
+ flex-direction: column;
+}
+
+.reset-form input,
+.reset-form button {
+ margin: 0.5rem 0;
+ padding: 0.75rem;
+ width: 100%;
+}
+
+.error-msg {
+ color: red;
+ margin-top: 0.5rem;
+}
+
+.success-msg {
+ color: green;
+ margin-top: 0.5rem;
+}
+
+@media (max-width: 600px) {
+ .reset-container {
+ padding: 0.5rem;
+ }
+}
diff --git a/frontend/cmmty/styles/settings.css b/frontend/cmmty/styles/settings.css
new file mode 100644
index 0000000..3d2e5f0
--- /dev/null
+++ b/frontend/cmmty/styles/settings.css
@@ -0,0 +1,21 @@
+.settings-container {
+ max-width: 600px;
+ margin: auto;
+ padding: 1rem;
+}
+
+.settings-section {
+ margin-bottom: 2rem;
+}
+
+input, button {
+ display: block;
+ width: 100%;
+ margin: 0.5rem 0;
+}
+
+@media (max-width: 600px) {
+ .settings-container {
+ padding: 0.5rem;
+ }
+}
diff --git a/frontend/cmmty/styles/verify-email.css b/frontend/cmmty/styles/verify-email.css
new file mode 100644
index 0000000..b8add1f
--- /dev/null
+++ b/frontend/cmmty/styles/verify-email.css
@@ -0,0 +1,18 @@
+.verify-container {
+ max-width: 500px;
+ margin: auto;
+ padding: 1rem;
+ text-align: center;
+}
+
+button {
+ margin-top: 1rem;
+ padding: 0.75rem;
+ width: 100%;
+}
+
+@media (max-width: 600px) {
+ .verify-container {
+ padding: 0.5rem;
+ }
+}