chore: setup project for production
This commit is contained in:
82
components/accounts/AccountSidebar.jsx
Normal file
82
components/accounts/AccountSidebar.jsx
Normal file
@@ -0,0 +1,82 @@
|
||||
'use client'
|
||||
import MainContext from "@/app/contexts/mainContext";
|
||||
import authAxios from "@/utils/axios";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import React, { useContext } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
const AccountSidebar = () => {
|
||||
const router = useRouter()
|
||||
const { setToken } = useContext(MainContext)
|
||||
const logoutFn = async () => {
|
||||
const response = await authAxios.post('/account/logout/')
|
||||
if (response.status == 200) {
|
||||
localStorage.removeItem('token')
|
||||
setToken(null)
|
||||
toast.success(response.data.message)
|
||||
router.push('/')
|
||||
}
|
||||
console.log(response)
|
||||
}
|
||||
return (
|
||||
<aside className="bg-white shadow-md overflow-x-auto md:w-64 md:min-h-screen">
|
||||
<div className="flex md:flex-col">
|
||||
|
||||
<nav className="flex md:flex-col whitespace-nowrap">
|
||||
<Link
|
||||
href="/accounts/profile"
|
||||
className="block py-2 px-4 text-gray-700 hover:bg-gray-200"
|
||||
>
|
||||
<span className="inline-block mr-2">👤</span> My profile
|
||||
</Link>
|
||||
<Link
|
||||
href="/accounts/profile/orders"
|
||||
className="block py-2 px-4 text-gray-700 hover:bg-gray-200"
|
||||
>
|
||||
<span className="inline-block mr-2">📦</span> Orders{" "}
|
||||
</Link>
|
||||
{/* <Link
|
||||
href="/accounts/profile/addresses"
|
||||
className="block py-2 px-4 text-gray-700 hover:bg-gray-200"
|
||||
>
|
||||
<span className="inline-block mr-2">📍</span> Addresses{" "}
|
||||
<span className="ml-2 inline-block bg-gray-200 rounded-full px-2 py-1 text-xs">
|
||||
1
|
||||
</span>
|
||||
</Link> */}
|
||||
<Link
|
||||
href="/accounts/profile/change-password"
|
||||
className="block py-2 px-4 text-gray-700 hover:bg-gray-200"
|
||||
>
|
||||
<span className="inline-block mr-2">🔑</span> Change password
|
||||
</Link>
|
||||
<button
|
||||
onClick={()=> logoutFn()}
|
||||
className="block py-2 px-4 text-gray-700 hover:bg-gray-200 pr-80"
|
||||
>
|
||||
<span className="inline-block mr-2">🚪</span> Logout
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
@media (max-width: 768px) {
|
||||
aside {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
aside::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
aside {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
</aside>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountSidebar;
|
||||
193
components/accounts/AddNewAddress.jsx
Normal file
193
components/accounts/AddNewAddress.jsx
Normal file
@@ -0,0 +1,193 @@
|
||||
'use client'
|
||||
import { useState } from 'react';
|
||||
import authAxios from '@/utils/axios';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const AddNewAddress = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
company: '',
|
||||
address: '',
|
||||
apartment: '',
|
||||
city: '',
|
||||
country: '',
|
||||
zipcode: '',
|
||||
phone: ''
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const response = await authAxios.post('/account/addresses/', formData);
|
||||
toast.success('Address added successfully')
|
||||
router.push('/accounts/profile/addresses')
|
||||
// Handle success (e.g., redirect or show success message)
|
||||
console.log('Address added successfully:', response.data);
|
||||
|
||||
|
||||
} catch (err) {
|
||||
setError(err.response?.data?.message || 'Failed to add address');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-semibold mb-6">Add a new address</h2>
|
||||
{error && <div className="mb-4 text-red-500">{error}</div>}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label htmlFor="first_name" className="block text-sm font-medium text-gray-700 mb-1">First name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="first_name"
|
||||
name="first_name"
|
||||
value={formData.first_name}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="last_name" className="block text-sm font-medium text-gray-700 mb-1">Last name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="last_name"
|
||||
name="last_name"
|
||||
value={formData.last_name}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="company" className="block text-sm font-medium text-gray-700 mb-1">Company</label>
|
||||
<input
|
||||
type="text"
|
||||
id="company"
|
||||
name="company"
|
||||
value={formData.company}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="address" className="block text-sm font-medium text-gray-700 mb-1">Address</label>
|
||||
<input
|
||||
type="text"
|
||||
id="address"
|
||||
name="address"
|
||||
value={formData.address}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="apartment" className="block text-sm font-medium text-gray-700 mb-1">Apartment, suite, etc.</label>
|
||||
<input
|
||||
type="text"
|
||||
id="apartment"
|
||||
name="apartment"
|
||||
value={formData.apartment}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="city" className="block text-sm font-medium text-gray-700 mb-1">City</label>
|
||||
<input
|
||||
type="text"
|
||||
id="city"
|
||||
name="city"
|
||||
value={formData.city}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="country" className="block text-sm font-medium text-gray-700 mb-1">Country/Region</label>
|
||||
<select
|
||||
id="country"
|
||||
name="country"
|
||||
value={formData.country}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
>
|
||||
<option value="">Select a country</option>
|
||||
<option value="IN">India</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="zipcode" className="block text-sm font-medium text-gray-700 mb-1">Postal/Zip Code</label>
|
||||
<input
|
||||
type="text"
|
||||
id="zipcode"
|
||||
name="zipcode"
|
||||
value={formData.zipcode}
|
||||
onChange={handleChange}
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">Phone</label>
|
||||
<div className="flex">
|
||||
<select className="p-2 border border-gray-300 rounded-l" style={{width: '80px'}}>
|
||||
<option value="IN">🇮🇳 +91</option>
|
||||
</select>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleChange}
|
||||
className="flex-grow p-2 border border-gray-300 rounded-r"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-end space-x-4">
|
||||
<button
|
||||
type="button"
|
||||
className="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="px-4 py-2 bg-[#96724f] text-white rounded hover:bg-[#AC8C6B] disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Submitting...' : 'Submit'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNewAddress;
|
||||
95
components/accounts/addresses.jsx
Normal file
95
components/accounts/addresses.jsx
Normal file
@@ -0,0 +1,95 @@
|
||||
'use client'
|
||||
import authAxios from '@/utils/axios';
|
||||
import { MapPin, Edit, Trash2 } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
const Addresses = () => {
|
||||
const [addresses, setAddresses] = useState([]);
|
||||
const [error, setError] = useState('');
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const fetchAddresses = async () => {
|
||||
try {
|
||||
const response = await authAxios.get('/account/addresses/');
|
||||
setAddresses(response.data);
|
||||
} catch (error) {
|
||||
setError('Failed to fetch addresses');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEdit = (id) => {
|
||||
router.push(`/accounts/profile/addresses/${id}`);
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!window.confirm('Are you sure you want to delete this address?')) return;
|
||||
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await authAxios.delete(`/account/addresses/${id}/`);
|
||||
setAddresses(addresses.filter(address => address.id !== id));
|
||||
} catch (error) {
|
||||
setError('Failed to delete address');
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchAddresses();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-3xl font-semibold border-b pb-4 text-zinc-700">Address</h1>
|
||||
|
||||
<Link href='/accounts/profile/new-address'>
|
||||
<div className='p-6 hover:border-yellow-400 border rounded-md w-fit mt-4 flex justify-center items-center flex-col'>
|
||||
<MapPin className='text-[#AC8C6B]' />
|
||||
<h1>Add new address</h1>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{error && <p className="text-red-500 mt-4">{error}</p>}
|
||||
|
||||
{addresses.map((address) => (
|
||||
<div key={address.id} className='border sm:p-6 p-2 w-3/6 mt-4 border-slate-800 relative'>
|
||||
<div className="absolute top-4 right-4 flex gap-2">
|
||||
<button
|
||||
onClick={() => handleEdit(address.id)}
|
||||
className="p-2 text-blue-600 hover:bg-blue-50 rounded-full"
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<Edit size={18} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(address.id)}
|
||||
className="p-2 text-red-600 hover:bg-red-50 rounded-full"
|
||||
disabled={isDeleting}
|
||||
>
|
||||
<Trash2 size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2 className='text-lg font-bold'>{address.first_name} {address.last_name}</h2>
|
||||
{address.company && <p className='text-md font-medium'>{address.company}</p>}
|
||||
<p className='text-md'>
|
||||
{address.address}
|
||||
{address.apartment && `, ${address.apartment}`}
|
||||
</p>
|
||||
<p className='text-md'>
|
||||
{address.city}, {address.country} - {address.zipcode}
|
||||
</p>
|
||||
<p className='text-md font-semibold'>Phone: {address.phone}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Addresses;
|
||||
209
components/accounts/editProfile.jsx
Normal file
209
components/accounts/editProfile.jsx
Normal file
@@ -0,0 +1,209 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import authAxios from '@/utils/axios';
|
||||
|
||||
const EditProfile = () => {
|
||||
const [profile, setProfile] = useState({
|
||||
email: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
phone: '',
|
||||
accepts_marketing: '',
|
||||
birth_day: '',
|
||||
gender: '',
|
||||
profile_pic: null,
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const fetchProfileData = async () => {
|
||||
const response = await authAxios.get('/account/profile');
|
||||
console.log(response)
|
||||
setProfile(response.data);
|
||||
};
|
||||
|
||||
fetchProfileData();
|
||||
}, []);
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setProfile((prevProfile) => ({
|
||||
...prevProfile,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleCheckboxChange = (e) => {
|
||||
setProfile((prevProfile) => ({
|
||||
...prevProfile,
|
||||
accepts_marketing: e.target.checked ? 'Yes' : 'No',
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
console.log(profile);
|
||||
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('email', profile.email);
|
||||
formData.append('first_name', profile.first_name);
|
||||
formData.append('last_name', profile.last_name);
|
||||
formData.append('phone', profile.phone);
|
||||
formData.append('accepts_marketing', profile.accepts_marketing);
|
||||
formData.append('birth_day', profile.birth_day);
|
||||
formData.append('gender', profile.gender);
|
||||
|
||||
|
||||
if (profile.profile_pic && profile.profile_pic instanceof File) {
|
||||
console.log('hello')
|
||||
formData.append('profile_pic', profile.profile_pic);
|
||||
}
|
||||
try {
|
||||
|
||||
const response = await authAxios.patch('/account/profile-update/', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
console.log(response)
|
||||
if (response.status === 200) {
|
||||
|
||||
router.push('/accounts/profile');
|
||||
} else {
|
||||
alert('Failed to update profile');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating profile:', error);
|
||||
alert('An error occurred while updating the profile.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
||||
<h2 className="text-2xl font-semibold mb-6">Edit Profile</h2>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label htmlFor="firstName" className="block text-sm font-medium text-gray-700 mb-1">First name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="firstName"
|
||||
name="first_name"
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
value={profile.first_name || ''}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="lastName" className="block text-sm font-medium text-gray-700 mb-1">Last name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="lastName"
|
||||
name="last_name"
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
value={profile.last_name || ''}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
value={profile.email || ''}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">Phone</label>
|
||||
<div className="flex">
|
||||
<select className="p-2 border border-gray-300 rounded-l" style={{ width: '80px' }}>
|
||||
<option value="IN" className='w-10 '>+91</option>
|
||||
</select>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
className="flex-grow p-2 border border-gray-300 rounded-r"
|
||||
value={profile.phone || ''}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="birthDay" className="block text-sm font-medium text-gray-700 mb-1">Birth Date</label>
|
||||
<input
|
||||
type="date"
|
||||
id="birthDay"
|
||||
name="birth_day"
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
value={profile.birth_day || ''}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="gender" className="block text-sm font-medium text-gray-700 mb-1">Gender</label>
|
||||
<select
|
||||
id="gender"
|
||||
name="gender"
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
value={profile.gender || ''}
|
||||
onChange={handleChange}
|
||||
>
|
||||
<option value="">Select Gender</option>
|
||||
<option value="Male">Male</option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="profilePic" className="block text-sm font-medium text-gray-700 mb-1">Profile Picture</label>
|
||||
<input
|
||||
type="file"
|
||||
id="profilePic"
|
||||
name="profile_pic"
|
||||
className="w-full p-2 border border-gray-300 rounded"
|
||||
onChange={(e) => setProfile({ ...profile, profile_pic: e.target.files[0] })}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label className="inline-flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="form-checkbox"
|
||||
checked={profile.accepts_marketing === 'Yes'}
|
||||
onChange={handleCheckboxChange}
|
||||
/>
|
||||
<span className="ml-2">Accepts Marketing</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end space-x-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push('/account/profile')}
|
||||
className="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-4 py-2 bg-[#96724f] text-white rounded hover:bg-[#AC8C6B]"
|
||||
>
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditProfile;
|
||||
Reference in New Issue
Block a user