chore: setup project for production

This commit is contained in:
afnaann
2025-02-19 17:00:55 +05:30
commit 12caeee710
271 changed files with 16199 additions and 0 deletions

View 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;

View 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;

View 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;

View 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;