implement authentication system with login, signup, and user module components and layout structure
This commit is contained in:
parent
350e632db6
commit
1e01ea3568
@ -86,16 +86,7 @@ const CoverLogin = () => {
|
|||||||
<ComponentsAuthLoginForm />
|
<ComponentsAuthLoginForm />
|
||||||
|
|
||||||
|
|
||||||
{/* Sign-up link */}
|
|
||||||
<div className="text-center dark:text-white">
|
|
||||||
Don't have an account?
|
|
||||||
<Link
|
|
||||||
href="/signup"
|
|
||||||
className="uppercase text-primary underline transition hover:text-black dark:hover:text-white"
|
|
||||||
>
|
|
||||||
SIGN UP
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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("✅ We’ve emailed you a reset code / link. Enter it below.");
|
setMessage("✅ We’ve emailed you a reset code / link. Enter it below.");
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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 = () => {
|
||||||
|
|||||||
@ -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: {
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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">
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user