Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,187 changes: 1,179 additions & 8 deletions frontend/package-lock.json

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.13.2",
"framer-motion": "^12.23.26",
"react": "^19.2.0",
"react-dom": "^19.2.0"
"react-dom": "^19.2.0",
"react-icons": "^5.5.0",
"react-router-dom": "^7.10.1"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"autoprefixer": "^10.4.22",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.18",
"vite": "^7.2.4"
}
}
6 changes: 6 additions & 0 deletions frontend/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
192 changes: 24 additions & 168 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,174 +1,30 @@
// src/App.jsx
import React from "react";
import { Routes, Route } from "react-router-dom";
import LandingPage from "./Pages/LandingPage";

function App() {
import Login from "./Authentication/Login";
import Signup from "./Authentication/Signup";
import ProductsPage from "./Pages/ProductsPage";
import ProductDetails from "./Pages/ProductsDetails";
import Cart from "./Pages/Cart";
import AboutUs from "./Pages/About";
import Contact from "./Pages/Contact";

export default function App() {
return (
<div className="min-h-screen bg-gray-50">
<LandingPage />
</div>
);
}

export default App;



// // src/App.jsx
// import { useState } from "react";
// import { motion } from "framer-motion";
// import Button from "./reusableComponents/Button";
// import Input from "./reusableComponents/Input";
// import ProductCard from "./reusableComponents/ProductCard";
// import Navbar from "./reusableComponents/Navbar";
// import Footer from "./reusableComponents/Footer";
// import BentoGrid from "./reusableComponents/BentoGrid";

// import FilterOptions from "./reusableComponents/FilterOption";
// import Modal from "./reusableComponents/ModalPopup";
// import Carousel from "./reusableComponents/CarouselSlider";
// import BannerCard from "./reusableComponents/BannerCard";
// import SearchBar from "./reusableComponents/Searchbar";

// const products = [
// { id: 1, name: "iPhone 15 Pro", price: 999, image: "https://images.unsplash.com/photo-1690489874296-ea89dd0ba3f3?w=300", category: "Electronics" },
// { id: 2, name: "MacBook Air", price: 1299, image: "https://images.unsplash.com/photo-1517336714731-489689fd1ca8?w=300", category: "Electronics" },
// { id: 3, name: "Nike Air Max", price: 129, image: "https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=300", category: "Clothing" },
// { id: 4, name: "Adidas Ultraboost", price: 189, image: "https://images.unsplash.com/photo-1549298916-b41d501d3772?w=300", category: "Clothing" },
// { id: 5, name: "Coffee Maker", price: 79, image: "https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=300", category: "Home" },
// { id: 6, name: "Yoga Mat", price: 39, image: "https://images.unsplash.com/photo-1588854337236-7947b26e6a62?w=300", category: "Sports" }
// ];

// function App() {
// const [cartCount, setCartCount] = useState(3);
// const [isModalOpen, setIsModalOpen] = useState(false);
// const [searchTerm, setSearchTerm] = useState("");
// const [filters, setFilters] = useState([]);
// const [email, setEmail] = useState("");

// const categories = ["Electronics", "Clothing", "Home", "Sports"];
// const carouselImages = [
// "https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=1200",
// "https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=1200",
// "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1200"
// ];
<Routes>
<Route path="/" element={<LandingPage />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />

// const filteredProducts = products.filter(product =>
// product.name.toLowerCase().includes(searchTerm.toLowerCase()) &&
// (filters.length === 0 || filters.includes(product.category))
// );
<Route path="/products" element={<ProductsPage />} />
<Route path="/product/:id" element={<ProductDetails />} />

// return (
// <div className="min-h-screen bg-gray-50">
// {/* Navbar */}
// <Navbar cartCount={cartCount} />

// <main className="max-w-7xl mx-auto px-4 py-12">
// {/* Hero Carousel */}
// <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} className="mb-12">
// <Carousel images={carouselImages} />
// </motion.div>

// {/* Search + Filters Row */}
// <div className="flex flex-col lg:flex-row gap-6 mb-12">
// <SearchBar onSearch={setSearchTerm} />
// <FilterOptions categories={categories} onFilterChange={setFilters} />
// </div>

// {/* Banner Cards */}
// <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
// <BannerCard
// title="Summer Sale 50% OFF"
// subtitle="Limited time offer on all electronics"
// ctaText="Shop Electronics"
// image="https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400"
// onClick={() => setFilters(["Electronics"])}
// />
// <BannerCard
// title="Free Shipping"
// subtitle="On orders over $50"
// ctaText="Learn More"
// image="https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=400"
// onClick={() => alert("Free shipping on orders over $50!")}
// />
// <BannerCard
// title="New Arrivals"
// subtitle="Check out latest products"
// ctaText="Explore Now"
// image="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400"
// onClick={() => setFilters([])}
// />
// </div>

// {/* Featured Products */}
// <section>
// <h2 className="text-3xl font-bold text-gray-900 mb-8">Featured Products</h2>
// <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
// {filteredProducts.map(product => (
// <ProductCard key={product.id} product={product} />
// ))}
// </div>
// </section>

// {/* Newsletter Signup */}
// <motion.section
// initial={{ opacity: 0 }}
// whileInView={{ opacity: 1 }}
// className="mt-20 p-8 bg-white rounded-2xl shadow-xl"
// >
// <h3 className="text-2xl font-bold text-center mb-6">Stay Updated</h3>
// <div className="flex flex-col sm:flex-row gap-4 max-w-md mx-auto">
// <Input
// label=""
// type="email"
// placeholder="Enter your email"
// value={email}
// onChange={(e) => setEmail(e.target.value)}
// />
// <Button size="lg" onClick={() => alert("Subscribed!")}>Subscribe</Button>
// </div>
// </motion.section>

// {/* Bento Grid Demo */}
// <section className="mt-20">
// <h3 className="text-2xl font-bold mb-8">Dashboard Preview</h3>
// <BentoGrid />
// </section>

// {/* Demo Modal Trigger */}
// <div className="mt-12 text-center">
// <Button
// size="lg"
// onClick={() => setIsModalOpen(true)}
// className="bg-gradient-to-r from-purple-600 to-blue-600 text-white"
// >
// Quick Product View
// </Button>
// </div>
// </main>

// {/* Modal */}
// <Modal
// isOpen={isModalOpen}
// onClose={() => setIsModalOpen(false)}
// title="iPhone 15 Pro - Quick View"
// >
// <div className="space-y-4">
// <img src="https://images.unsplash.com/photo-1690489874296-ea89dd0ba3f3?w=400" alt="iPhone" className="w-full h-48 object-cover rounded-lg" />
// <h3 className="text-xl font-bold">$999</h3>
// <p className="text-gray-600">Latest iPhone with A17 Pro chip and titanium design.</p>
// <div className="flex gap-3 pt-4">
// <Button className="flex-1">Add to Cart</Button>
// <Button variant="outline" className="flex-1">View Details</Button>
// </div>
// </div>
// </Modal>

// {/* Footer */}
// <Footer />
// </div>
// );
// }

// export default App;
<Route path="/cart" element={<Cart />} />

{/* NEW ROUTES */}
<Route path="/about" element={<AboutUs />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</div>
);
}
47 changes: 47 additions & 0 deletions frontend/src/Authentication/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Login.jsx
import React, { useState } from "react";
import Input from "../reusableComponents/Input";
import { useNavigate, Link } from "react-router-dom";
import { loginUser } from "../Services/authService";

export default function Login() {
const [form, setForm] = useState({ identifier: "", password: "" });
const [errors, setErrors] = useState({});
const navigate = useNavigate();

const handleLogin = async (e) => {
e.preventDefault();

let newErrors = {};
if (!form.identifier) newErrors.identifier = "Email or Username required";
if (!form.password) newErrors.password = "Password required";
setErrors(newErrors);
if (Object.keys(newErrors).length !== 0) return;

try {
const res = await loginUser(form);
localStorage.setItem("token", res.data.access_token);
navigate("/products");
} catch (err) {
console.error(err.response);
alert(err.response?.data?.detail || "Invalid credentials");
}
};

return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 px-4">
<form onSubmit={handleLogin} className="space-y-4 max-w-md w-full p-6 bg-white shadow rounded">
<Input label="Email or Username" value={form.identifier} onChange={(e) => setForm({ ...form, identifier: e.target.value })} placeholder="Enter email or username" error={errors.identifier} />
<Input label="Password" type="password" value={form.password} onChange={(e) => setForm({ ...form, password: e.target.value })} placeholder="Enter password" error={errors.password} />

<button className="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700 transition">
Login
</button>

<p className="text-center text-sm">
Don’t have an account? <Link to="/signup" className="text-blue-600">Sign Up</Link>
</p>
</form>
</div>
);
}
82 changes: 82 additions & 0 deletions frontend/src/Authentication/Signup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Signup.jsx
import React, { useState } from "react";
import Input from "../reusableComponents/Input";
import { useNavigate, Link } from "react-router-dom";
import { registerUser } from "../Services/authService";

export default function Signup() {
const [form, setForm] = useState({
fullname: "",
username: "",
email: "",
mobile: "",
password: "",
confirm_password: "",
role: "user",
});
const [errors, setErrors] = useState({});
const navigate = useNavigate();

const handleChange = (key, value) => setForm({ ...form, [key]: value });

const handleSignup = async (e) => {
e.preventDefault();

// Frontend validation
let newErrors = {};
if (!form.fullname) newErrors.fullname = "Full name is required";
if (!form.username) newErrors.username = "Username is required";
if (!form.email) newErrors.email = "Email is required";
if (!form.password) newErrors.password = "Password is required";
if (!form.confirm_password) newErrors.confirm_password = "Confirm password is required";
if (form.password !== form.confirm_password) newErrors.confirm_password = "Passwords do not match";
setErrors(newErrors);

if (Object.keys(newErrors).length !== 0) return;

const payload = {
fullname: form.fullname,
username: form.username,
email: form.email,
mobile: form.mobile,
password: form.password,
confirm_password: form.confirm_password,
};

try {
await registerUser(form.role, payload);
alert("Registration successful");
navigate("/login");
} catch (err) {
console.error(err.response);
alert(err.response?.data?.detail || err.response?.data || "Registration failed");
}
};

return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 px-4">
<form onSubmit={handleSignup} className="space-y-4 max-w-md w-full p-6 bg-white shadow rounded">
<Input label="Full Name" value={form.fullname} onChange={(e) => handleChange("fullname", e.target.value)} placeholder="Enter full name" error={errors.fullname} />
<Input label="Username" value={form.username} onChange={(e) => handleChange("username", e.target.value)} placeholder="Enter username" error={errors.username} />
<Input label="Email" type="email" value={form.email} onChange={(e) => handleChange("email", e.target.value)} placeholder="Enter email" error={errors.email} />
<Input label="Mobile" value={form.mobile} onChange={(e) => handleChange("mobile", e.target.value)} placeholder="Enter mobile number" error={errors.mobile} />
<Input label="Password" type="password" value={form.password} onChange={(e) => handleChange("password", e.target.value)} placeholder="Enter password" error={errors.password} />
<Input label="Confirm Password" type="password" value={form.confirm_password} onChange={(e) => handleChange("confirm_password", e.target.value)} placeholder="Confirm password" error={errors.confirm_password} />

<select className="w-full border p-2 rounded" value={form.role} onChange={(e) => handleChange("role", e.target.value)}>
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="technician">Technician</option>
</select>

<button className="w-full bg-green-600 text-white py-2 rounded hover:bg-green-700 transition">
Sign Up
</button>

<p className="text-center text-sm">
Already have an account? <Link to="/login" className="text-blue-600">Login</Link>
</p>
</form>
</div>
);
}
Loading