implement authentication system with login, signup, and user module components and layout structure

This commit is contained in:
Alaguraj0361 2026-04-17 14:47:49 +05:30
parent 350e632db6
commit 1e01ea3568
10 changed files with 33 additions and 57 deletions

View File

@ -86,16 +86,7 @@ const CoverLogin = () => {
<ComponentsAuthLoginForm /> <ComponentsAuthLoginForm />
{/* Sign-up link */}
<div className="text-center dark:text-white">
Don&apos;t have an account?&nbsp;
<Link
href="/signup"
className="uppercase text-primary underline transition hover:text-black dark:hover:text-white"
>
SIGN UP
</Link>
</div>
</div> </div>
{/* Footer */} {/* Footer */}

View File

@ -1,6 +1,6 @@
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
const AUTH_API_BASE = process.env.AUTH_API_BASE ?? 'https://ebay.backend.vgproducts.com'; const AUTH_API_BASE = process.env.AUTH_API_BASE ?? 'http://localhost:3050';
const SESSION_MAX_AGE_S = 30 * 60; // 30 minutes in seconds const SESSION_MAX_AGE_S = 30 * 60; // 30 minutes in seconds
// Utility to extract userId from a nested object // Utility to extract userId from a nested object

View File

@ -1,6 +1,6 @@
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
const AUTH_API_BASE = process.env.AUTH_API_BASE ?? 'https://ebay.backend.vgproducts.com'; const AUTH_API_BASE = process.env.AUTH_API_BASE ?? 'http://localhost:3050';
export async function POST(req: Request) { export async function POST(req: Request) {
const body = await req.json(); const body = await req.json();

View File

@ -13,7 +13,7 @@ export default function ForgotPasswordForm() {
setLoading(true); setLoading(true);
setMessage(""); setMessage("");
try { try {
const res = await axios.post("https://ebay.backend.vgproducts.com/api/auth/forgot-password", { email }); const res = await axios.post("http://localhost:3050/api/auth/forgot-password", { email });
setMessage("✅ Weve emailed you a reset code / link. Enter it below."); setMessage("✅ Weve emailed you a reset code / link. Enter it below.");
} catch (err: any) { } catch (err: any) {
console.error(err); console.error(err);

View File

@ -7,7 +7,7 @@ import IconMail from '@/components/icon/icon-mail';
const ComponentsAuthLoginForm = () => { const ComponentsAuthLoginForm = () => {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const nextUrl = searchParams.get('next') || '/'; const nextUrl = searchParams.get('next') || '/user';
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
@ -29,7 +29,7 @@ const ComponentsAuthLoginForm = () => {
setLoading(true); setLoading(true);
try { try {
// ✅ Call your Next.js API (same origin), which sets d4a_uid, d4a_session, d4a_exp cookies // ✅ Call your Next.js API (same origin), which sets d4a_uid, d4a_session, d4a_exp cookies
const res = await fetch('https://ebay.backend.vgproducts.com/api/auth/login', { const res = await fetch('http://localhost:3050/api/auth/login', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
// credentials not required for same-origin, but harmless: // credentials not required for same-origin, but harmless:

View File

@ -7,8 +7,8 @@ import IconUser from '@/components/icon/icon-user';
const API_BASE = const API_BASE =
process.env.NEXT_PUBLIC_API_BASE_URL?.replace(/\/$/, '') || process.env.NEXT_PUBLIC_API_BASE_URL?.replace(/\/$/, '') ||
// 'https://ebay.backend.vgproducts.com'; // 'http://localhost:3050';
'https://ebay.backend.vgproducts.com'; 'http://localhost:3050';
const ComponentsAuthRegisterForm = () => { const ComponentsAuthRegisterForm = () => {

View File

@ -40,7 +40,7 @@ const ComponentsAuthChangePasswordForm = () => {
// ✅ Axios call with generic type // ✅ Axios call with generic type
const res = await axios.post<ChangePasswordResponse>( const res = await axios.post<ChangePasswordResponse>(
`https://ebay.backend.vgproducts.com/api/auth/change-password`, `http://localhost:3050/api/auth/change-password`,
formData, formData,
{ {
headers: { headers: {

View File

@ -54,7 +54,7 @@ const Header = () => {
setUser(email); setUser(email);
} else { } else {
axios axios
.get("https://ebay.backend.vgproducts.com/api/auth/protected", { withCredentials: true }) .get("http://localhost:3050/api/auth/protected", { withCredentials: true })
.then((res: any) => { .then((res: any) => {
const userData = res.data.user; const userData = res.data.user;
if (userData) { if (userData) {
@ -218,9 +218,7 @@ const Header = () => {
<IconMenu className="h-5 w-5" /> <IconMenu className="h-5 w-5" />
</button> </button>
<Link href="/" className="main-logo flex shrink-0 items-center ml-3"> <Link href="/" className="main-logo flex shrink-0 items-center ml-3">
<img className="inline w-[164px] h-[40px] ltr:-ml-1 rtl:-mr-1 block dark:hidden" src="/assets/images/logo_dark.png" alt="logo" /> <span className="align-middle text-2xl font-bold transition-all duration-300 ltr:ml-1.5 rtl:mr-1.5 dark:text-white-light md:inline text-[#e8572a]">VG Products</span>
<img className="inline w-[164px] h-[40px] ltr:-ml-1 rtl:-mr-1 hidden dark:block" src="/assets/images/logo_light.png" alt="logo" />
{/* <span className="hidden align-middle text-2xl font-semibold transition-all duration-300 ltr:ml-1.5 rtl:mr-1.5 dark:text-white-light md:inline">VG Products</span> */}
</Link> </Link>
</div> </div>

View File

@ -66,10 +66,8 @@ const Sidebar = () => {
> >
<div className="h-full bg-white dark:bg-black"> <div className="h-full bg-white dark:bg-black">
<div className="flex items-center justify-between px-4 py-3"> <div className="flex items-center justify-between px-4 py-3">
<Link href="/" className="main-logo flex shrink-0 items-center"> <Link href="/user" className="main-logo flex shrink-0 items-center">
<img className="ml-[5px] w-[164px] h-[40px] flex-none block dark:hidden" src="/assets/images/logo_dark.png" alt="logo" /> <span className="align-middle text-2xl font-bold ltr:ml-1.5 rtl:mr-1.5 dark:text-white-light lg:inline text-[#e8572a]">VG Products</span>
<img className="ml-[5px] w-[164px] h-[40px] flex-none hidden dark:block" src="/assets/images/logo_light.png" alt="logo" />
{/* <span className="align-middle text-2xl font-semibold ltr:ml-1.5 rtl:mr-1.5 dark:text-white-light lg:inline">VG Products</span> */}
</Link> </Link>
<button <button
type="button" type="button"
@ -96,21 +94,9 @@ const Sidebar = () => {
</div> </div>
</button> </button>
<AnimateHeight duration={300} height={currentMenu === 'dashboard' ? 'auto' : 0}> */} <AnimateHeight duration={300} height={currentMenu === 'dashboard' ? 'auto' : 0}> */}
<li className="nav-item"> {/* Dashboard removed */}
<Link href="/" className="group nav-link">
<div className="flex items-center">
<span className="shrink-0 group-hover:!text-primary">🏠</span>
<span className="text-black ltr:pl-3 rtl:pr-3 dark:text-[#506690] dark:group-hover:text-white-dark">{t('Dashboard')}</span>
</div>
</Link>
</li>
{/* </AnimateHeight>
</li> */}
<h2 className="-mx-4 mb-1 flex items-center bg-[#0f2444] px-7 py-3 font-extrabold uppercase dark:bg-dark dark:bg-opacity-[0.08]"> {/* Settings header removed */}
<IconMinus className="hidden h-5 w-4 flex-none" />
<span>{t('settings')}</span>
</h2>
<li className="nav-item"> <li className="nav-item">
<Link href="/user" className="group nav-link"> <Link href="/user" className="group nav-link">

View File

@ -14,11 +14,11 @@ const UserModule = () => {
const [addUserModal, setAddUserModal] = useState(false); const [addUserModal, setAddUserModal] = useState(false);
const defaultParams = { const defaultParams = {
userid: null, _id: null,
name: '', name: '',
email: '', email: '',
password: '', password: '',
phonenumber: '', mobileNumber: '',
role: 'customer', role: 'customer',
}; };
const [params, setParams] = useState<any>({ ...defaultParams }); const [params, setParams] = useState<any>({ ...defaultParams });
@ -26,9 +26,10 @@ const UserModule = () => {
// ✅ Fetch users // ✅ Fetch users
const fetchUsers = async () => { const fetchUsers = async () => {
try { try {
const res = await axios.get('https://ebay.backend.vgproducts.com/api/auth/users'); const res = await axios.get('http://localhost:3050/api/users');
setUsers(res.data?.users || []); const usersList = Array.isArray(res.data) ? res.data : (res.data?.users || []);
setFilteredUsers(res.data?.users || []); setUsers(usersList);
setFilteredUsers(usersList);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
showMessage('Failed to load users', 'error'); showMessage('Failed to load users', 'error');
@ -72,19 +73,19 @@ const UserModule = () => {
// ✅ Add / Update User // ✅ Add / Update User
const saveUser = async () => { const saveUser = async () => {
if (!params.name || !params.email || !params.phonenumber || !params.role) { if (!params.name || !params.email || !params.role) {
showMessage('Please fill all required fields', 'error'); showMessage('Please fill all required fields', 'error');
return; return;
} }
try { try {
if (params.userid) { if (params._id) {
// UPDATE // UPDATE
await axios.put(`https://ebay.backend.vgproducts.com/api/auth/users/${params.userid}`, params); await axios.put(`http://localhost:3050/api/users/${params._id}`, params);
showMessage('User updated successfully'); showMessage('User updated successfully');
} else { } else {
// ADD // ADD
await axios.post('https://ebay.backend.vgproducts.com/api/auth/users/add', params); await axios.post('http://localhost:3050/api/users/create', params);
showMessage('User added successfully'); showMessage('User added successfully');
} }
setAddUserModal(false); setAddUserModal(false);
@ -113,7 +114,7 @@ const UserModule = () => {
}).then(async (result) => { }).then(async (result) => {
if (result.isConfirmed) { if (result.isConfirmed) {
try { try {
await axios.delete(`https://ebay.backend.vgproducts.com/api/auth/users/${user.userid}`); await axios.delete(`http://localhost:3050/api/users/${user._id}`);
showMessage('User deleted successfully'); showMessage('User deleted successfully');
fetchUsers(); fetchUsers();
} catch (err) { } catch (err) {
@ -174,10 +175,10 @@ const UserModule = () => {
</thead> </thead>
<tbody> <tbody>
{filteredUsers.map((user: any) => ( {filteredUsers.map((user: any) => (
<tr key={user.userid}> <tr key={user._id}>
<td>{user.name}</td> <td>{user.name}</td>
<td>{user.email}</td> <td>{user.email}</td>
<td>{user.phonenumber}</td> <td>{user.mobileNumber}</td>
<td>{user.role}</td> <td>{user.role}</td>
<td className="text-center"> <td className="text-center">
<div className="flex justify-center gap-2"> <div className="flex justify-center gap-2">
@ -241,7 +242,7 @@ const UserModule = () => {
<IconX /> <IconX />
</button> </button>
<div className="bg-[#fbfbfb] py-3 text-lg font-medium ltr:pl-5 rtl:pr-5 dark:bg-[#121c2c]"> <div className="bg-[#fbfbfb] py-3 text-lg font-medium ltr:pl-5 rtl:pr-5 dark:bg-[#121c2c]">
{params.userid ? 'Edit User' : 'Add User'} {params._id ? 'Edit User' : 'Add User'}
</div> </div>
<div className="p-5"> <div className="p-5">
<form> <form>
@ -267,7 +268,7 @@ const UserModule = () => {
placeholder="Enter email" placeholder="Enter email"
/> />
</div> </div>
{!params.userid && ( {!params._id && (
<div className="mb-4"> <div className="mb-4">
<label>Password</label> <label>Password</label>
<input <input
@ -283,10 +284,10 @@ const UserModule = () => {
<div className="mb-4"> <div className="mb-4">
<label>Phone Number</label> <label>Phone Number</label>
<input <input
id="phonenumber" id="mobileNumber"
type="text" type="text"
className="form-input" className="form-input"
value={params.phonenumber} value={params.mobileNumber || ''}
onChange={changeValue} onChange={changeValue}
placeholder="Enter phone number" placeholder="Enter phone number"
/> />
@ -316,7 +317,7 @@ const UserModule = () => {
className="btn btn-primary ltr:ml-4 rtl:mr-4" className="btn btn-primary ltr:ml-4 rtl:mr-4"
onClick={saveUser} onClick={saveUser}
> >
{params.userid ? 'Update' : 'Add'} {params._id ? 'Update' : 'Add'}
</button> </button>
</div> </div>
</form> </form>