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

155
app/accounts/login/page.jsx Normal file
View File

@@ -0,0 +1,155 @@
"use client";
import MainContext from "@/app/contexts/mainContext";
import Image from "next/image";
import React, { useContext, useState } from "react";
const LoginSignup = () => {
const [isLogin, setIsLogin] = useState(true);
const { loginUser, registerUser } = useContext(MainContext);
const [formData, setFormData] = useState({
email: "",
password: "",
confirmPassword: "",
});
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormData((prevData) => ({
...prevData,
[name]: value,
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
if (isLogin) {
loginUser(formData);
} else {
registerUser(formData);
}
};
const toggleMode = () => {
setIsLogin(!isLogin);
setFormData({ email: "", password: "", confirmPassword: "" });
};
return (
<div className="flex h-screen bg-gray-100">
{/* Left section with video */}
<div className="hidden lg:flex lg:w-[60%] bg-cover bg-center">
<video
src="/loginvideo.mp4"
className="object-cover w-full h-full"
autoPlay
loop
muted
playsInline
>
<source src="/loginvideo.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
{/* Right section with login/signup form */}
<div className="w-full lg:w-1/2 flex items-center justify-center p-8 ">
<div className="max-w-md w-full space-y-8 bg-white p-7">
<div className="flex gap-4 items-center justify-center">
<Image
src={"/logo1.jpg"}
height={200}
width={200}
alt="logo"
className="h-16 w-16 text-center "
/>
<h1 className="text-[#AC8C6B] text-3xl">{`${
isLogin ? "Login" : "Sign up"
}`}</h1>
</div>
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px flex flex-col gap-5">
<div>
<label htmlFor="email-address" className="sr-only">
Email address
</label>
<input
id="email-address"
name="email"
type="email"
autoComplete="email"
required
className="appearance-none rounded-none relative block w-full px-3 py-3 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-[#C19A5B] focus:border-[#C19A5B] focus:z-10 sm:text-sm"
placeholder="Email address"
value={formData.email}
onChange={handleInputChange}
/>
</div>
<div>
<label htmlFor="password" className="sr-only">
Password
</label>
<input
id="password"
name="password"
type="password"
autoComplete="current-password"
required
className="appearance-none rounded-none relative block w-full px-3 py-3 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-[#C19A5B] focus:border-[#C19A5B] focus:z-10 sm:text-sm"
placeholder="Password"
value={formData.password}
onChange={handleInputChange}
/>
</div>
{!isLogin && (
<div>
<label htmlFor="confirm-password" className="sr-only">
Confirm Password
</label>
<input
id="confirm-password"
name="confirmPassword"
type="password"
autoComplete="new-password"
required
className="appearance-none rounded-none relative block w-full px-3 py-3 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-[#C19A5B] focus:border-[#C19A5B] focus:z-10 sm:text-sm"
placeholder="Confirm Password"
value={formData.confirmPassword}
onChange={handleInputChange}
/>
</div>
)}
</div>
<div>
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-7 border border-transparent text-xl text-[300] font-medium text-white bg-[#C19A5B] hover:bg-[#c49d60] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
{isLogin ? "Login" : "Sign up"}
</button>
</div>
</form>
<div className="text-center">
<h2 className="text-[#AC8C6B] font-semibold">
Manage subscriptions
</h2>
<h2 className="text-xl my-3">or</h2>
<h2 className="capitalize border-2 px-5 py-3">
Continue with google
</h2>
<button
onClick={toggleMode}
className="font-medium text-[#C19A5B] mt-10"
>
{isLogin
? "Don't have an account? Sign up"
: "Already have an account? Sign in"}
</button>
</div>
</div>
</div>
</div>
);
};
export default LoginSignup;

View File

@@ -0,0 +1,204 @@
'use client'
import { useState, useEffect } from 'react';
import { useParams, useRouter } from 'next/navigation';
import authAxios from '@/utils/axios';
const EditAddress = () => {
const { id } = useParams();
const router = useRouter();
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [formData, setFormData] = useState({
first_name: '',
last_name: '',
company: '',
address: '',
apartment: '',
city: '',
country: '',
zipcode: '',
phone: ''
});
useEffect(() => {
const fetchAddress = async () => {
try {
const response = await authAxios.get(`/account/addresses/${id}/`);
setFormData(response.data);
} catch (err) {
setError('Failed to fetch address details');
console.error(err);
}
};
if (id) {
fetchAddress();
}
}, [id]);
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
setError('');
try {
await authAxios.put(`/account/addresses/${id}/`, formData);
router.push('/accounts/profile/addresses');
} catch (err) {
setError(err.response?.data?.message || 'Failed to update address');
} finally {
setLoading(false);
}
};
return (
<div className="bg-white p-6 rounded-lg shadow-md">
<h2 className="text-2xl font-semibold mb-6">Edit 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"
onClick={() => router.push('/accounts/profile/addresses')}
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 ? 'Updating...' : 'Update Address'}
</button>
</div>
</form>
</div>
);
};
export default EditAddress;

View File

@@ -0,0 +1,16 @@
'use client'
import Addresses from '@/components/accounts/addresses';
import React from 'react';
const Page = () => {
return (
<div>
{/* <Asddresses/> */}
</div>
);
};
export default Page;

View File

@@ -0,0 +1,113 @@
'use client'
import React, { useState } from "react";
import axios from "axios";
import authAxios from "@/utils/axios";
const ChangePassword = () => {
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmNewPassword, setConfirmNewPassword] = useState("");
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
if (!oldPassword || !newPassword || !confirmNewPassword) {
setError("All fields are required.");
return;
}
if (newPassword !== confirmNewPassword) {
setError("New passwords do not match.");
return;
}
setError("");
try {
const response = await authAxios.post(
"/account/change-password/",
{
old_password: oldPassword,
new_password: newPassword,
confirm_new_password: confirmNewPassword,
}
);
if (response.data.status) {
setSuccess("Password updated successfully.");
} else {
setError(response.data.message || "Something went wrong.");
}
} catch (err) {
setError("Failed to change password. Please try again.");
}
};
return (
<div>
<h1 className="text-3xl font-semibold border-b pb-4 text-zinc-700">
Change Password
</h1>
<form onSubmit={handleSubmit}>
<div className="sm:w-3/4 p-4">
<label className="block font-medium my-3 text-zinc-700 sm:text-xl text-lg">
Old Password
</label>
<input
type="password"
className="mt-1 block w-full sm:w-3/5 rounded-md p-3 border"
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
<label className="block font-medium my-3 text-zinc-700 sm:text-xl text-lg">
New Password
</label>
<input
type="password"
className="mt-1 block w-full sm:w-3/5 rounded-md p-3 border"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
<label className="block my-3 font-medium text-zinc-700 sm:text-xl text-lg">
Confirm New Password
</label>
<input
type="password"
className="mt-1 block w-full sm:w-3/5 rounded-md p-3 border"
value={confirmNewPassword}
onChange={(e) => setConfirmNewPassword(e.target.value)}
/>
{error && (
<div className="text-red-600 mt-2">
<p>{error}</p>
</div>
)}
{success && (
<div className="text-green-600 mt-2">
<p>{success}</p>
</div>
)}
<button
type="submit"
className="bg-[#AC8C6B] bg-opacity-70 text-white px-4 py-3 rounded-md mt-3"
>
Submit
</button>
</div>
</form>
</div>
);
};
export default ChangePassword;

View File

@@ -0,0 +1,12 @@
import EditProfile from '@/components/accounts/editProfile'
import React from 'react'
const page = () => {
return (
<div>
<EditProfile/>
</div>
)
}
export default page

View File

@@ -0,0 +1,20 @@
import AccountSidebar from "@/components/accounts/AccountSidebar";
import Link from "next/link";
export const metadata = {
title: "Accounts",
};
export default function layout({ children }) {
return (
<div className='bg-slate-50'>
<div className="flex flex-col sm:flex-row min-h-screen bg-gray-100 max-w-7xl mx-auto">
{/* Sidebar - flex row on mobile, column on desktop */}
<AccountSidebar />
{/* Main content */}
<main className="flex-1 p-8">{children}</main>
</div>
</div>
);
}

View File

@@ -0,0 +1,12 @@
import AddNewAddress from '@/components/accounts/AddNewAddress'
import React from 'react'
const page = () => {
return (
<div>
{/* <AddNewAddress /> */}
</div>
)
}
export default page

View File

@@ -0,0 +1,120 @@
"use client";
import { useCurrency } from "@/app/contexts/currencyContext";
import authAxios, { backendUrl } from "@/utils/axios";
import { ShoppingBag } from "lucide-react";
import Image from "next/image";
import { useState, useEffect } from "react";
const OrdersPage = () => {
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(true);
const { isLoading, formatPrice } = useCurrency();
useEffect(() => {
const fetchOrders = async () => {
try {
const response = await authAxios.get("/orders/my-orders");
setOrders(response.data);
} catch (error) {
console.error("Error fetching orders:", error);
} finally {
setLoading(false);
}
};
fetchOrders();
}, []);
if (loading) {
return (
<div className="flex justify-center items-center min-h-[40vh]">
Loading...
</div>
);
}
if (!orders.length) {
return (
<div>
<h1 className="text-3xl font-semibold border-b pb-4 text-zinc-700">
Orders
</h1>
<div className="flex flex-col gap-5 justify-center items-center min-h-[40vh]">
<h2>
<ShoppingBag className="sm:ml-20 mb-3 text-[#AC8C6B]" />
You don&apos;t have any orders yet
</h2>
</div>
</div>
);
}
return (
<div>
<h1 className="text-3xl font-semibold border-b pb-4 text-zinc-700">
Orders
</h1>
<div className="mt-6 space-y-4">
{orders.map((order) => (
<div key={order.id} className="bg-white p-4 rounded-lg shadow">
<div className="flex justify-between items-center mb-4">
<span className="font-medium">Order #{order.order_number}</span>
<span
className={`px-3 py-1 rounded-full text-sm ${
order.status === "DELIVERED"
? "bg-green-100 text-green-800"
: order.status === "SHIPPED"
? "bg-blue-100 text-blue-800"
: "bg-yellow-100 text-yellow-800"
}`}
>
{order.status}
</span>
</div>
<div className="space-y-3">
{order.items.map((item) => (
<div key={item.id} className="flex items-center gap-4">
<Image
src={`${backendUrl}${item.variant.product.images[0].image}`}
alt={item.variant.product.product_name}
width={64}
height={64}
className="object-cover rounded-md"
/>
<div>
<p className="font-medium">
{item.variant.product.product_name}
</p>
<p className="text-sm text-gray-600">
Size: {item.variant.size.size_name}
{item.design && ` • Design: ${item.design.design_name}`}
</p>
<p className="text-sm text-gray-600">
Quantity: {item.quantity}
</p>
</div>
</div>
))}
</div>
<div className="mt-4 pt-4 border-t flex justify-between items-center">
<div className="text-gray-600">
{new Date(order.created_at).toLocaleDateString()}
</div>
<div className="font-medium">
Total:{" "}
{isLoading ? (
<span>Loading...</span>
) : (
<span>{formatPrice(order.total_amount)}</span>
)}
</div>
</div>
</div>
))}
</div>
</div>
);
};
export default OrdersPage;

View File

@@ -0,0 +1,151 @@
"use client";
import React, { useState, useEffect } from "react";
import axios from "axios";
import authAxios from "@/utils/axios";
import { useRouter } from "next/navigation";
import Image from "next/image";
export default function ProfilePage() {
const [profileData, setProfileData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
const router = useRouter();
useEffect(() => {
const fetchProfileData = async () => {
try {
const response = await authAxios.get("/account/profile/");
setProfileData(response.data);
setIsLoading(false);
} catch (error) {
console.error("Error fetching profile data", error);
setIsError(true);
setIsLoading(false);
}
};
fetchProfileData();
}, []);
const getDisplayValue = (value) => {
return value ? value : "Not set up";
};
if (isLoading) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error loading profile data. Please try again later.</div>;
}
return (
<div className="max-h-[70vh] hide-navbar overflow-y-scroll">
<div className="flex gap-3">
<div className="p-6 sm:text-xl border">
All Orders{" "}
<h2 className="text-lg sm:text-2xl font-semibold">
{profileData.orders || 0}
</h2>
</div>
<div className="p-6 border rounded-md sm:text-xl ">
Addresses{" "}
<h2 className="text-lg sm:text-2xl font-semibold">
{profileData.addresses || 0}
</h2>
</div>
</div>
<h1 className="text-2xl font-bold mb-4 mt-4">Profile information</h1>
<div className="bg-white shadow-md rounded p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
First name
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.first_name)}
</p>
</div>
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Last name
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.last_name)}
</p>
</div>
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Date of birth
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.birth_day)}
</p>
</div>
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Gender
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.gender)}
</p>
</div>
</div>
<h2 className="text-xl font-bold mt-6 mb-4">Contact methods</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Email
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.email)}
</p>
</div>
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Phone
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.phone)}
</p>
</div>
</div>
<h2 className="text-xl font-bold mt-6 mb-4">Other Info</h2>
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Profile Picture
</label>
{profileData.profile_pic ? (
<Image
src={profileData.profile_pic}
alt="Profile Picture"
width={64}
height={64}
className="rounded-full object-cover"
/>
) : (
<p className="text-gray-900">Not set up</p>
)}
</div>
<h2 className="text-xl font-bold mt-6 mb-4">Other Info</h2>
<div>
<label className="block text-gray-700 text-sm font-bold mb-2">
Accepts Marketing from Gupta Rudraksha
</label>
<p className="text-gray-900">
{getDisplayValue(profileData.accepts_marketing)}
</p>
</div>
<button
onClick={() => router.push("/accounts/profile/edit-profile")}
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-6"
>
Edit
</button>
</div>
</div>
);
}