Skip to content

Commit

Permalink
Purchase Page
Browse files Browse the repository at this point in the history
  • Loading branch information
shawakash committed Aug 18, 2023
1 parent e862c77 commit 720997c
Show file tree
Hide file tree
Showing 15 changed files with 257 additions and 28 deletions.
6 changes: 3 additions & 3 deletions apps/api/src/db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ const productSchema = new mongoose.Schema({
imageUrls: [{
type: String,
}],
creator: {type: mongoose.Schema.Types.ObjectId, ref: 'Admin'},
purchaser: [{type: mongoose.Schema.Types.ObjectId, ref: 'User'}]
creator: {type: mongoose.Schema.Types.ObjectId, ref: 'store_admin'},
purchaser: [{type: mongoose.Schema.Types.ObjectId, ref: 'store_user'}]
});

const purchaseSchema = new mongoose.Schema({
product: {type: mongoose.Schema.Types.ObjectId, ref: 'Product', required: true },
product: {type: mongoose.Schema.Types.ObjectId, ref: 'product', required: true },
quantity: {type: Number, required: true},
address: {type: String, required: true},
phone: {type: Number, required: true},
Expand Down
22 changes: 13 additions & 9 deletions apps/api/src/route/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const userAuth = (req: Request, res: Response, next: NextFunction) => {
next();
})
} catch (error) {
return res.status(500).json({ message: "Internal Error", err: error });
return res.status(500).json({ message: "Internal Error from auth", err: error });
}
}

Expand Down Expand Up @@ -139,23 +139,27 @@ route.post('/prod/:prodId', userAuth, async (req, res) => {
await userData.save();
const total = parsedInput.data.quantity * prod.price;
const leger = new Leger({...parsedInput.data, product: prod._id, total, buyer: userId, seller: prod.creator});
await leger.save();
(await leger.save()).populate('product');
return res.status(200).json({ message: 'Product purchased successfully', leger });
} else {
return res.status(500).json({ message: "Internal Server Error" });
}
});

route.get('/prod/purchased', userAuth, async (req, res) => {
try {
log("userId");
route.get('/purchased', userAuth, async (req, res) => {
const { userId } = req.headers;
const data = await Leger.find({buyer: userId}).populate("product");
const data = await Leger.find({buyer: userId}).populate('product');
return res.status(200).json({ purchasedProd: data });
} catch (error) {
return res.status(500).json({ message: "Internal Server Error" });
}
});

route.delete('/purchased/:legerId', userAuth, async (req, res) => {
const { legerId } = req.params;
const leger = await Leger.findByIdAndDelete(legerId);
if(!leger) {
return res.status(404).json({ message: 'No such Leger' });
}
return res.status(200).json({ message: 'Leger Deleted' });
})

export default route;

3 changes: 2 additions & 1 deletion apps/user/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import "./App.css";
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import { Toaster } from "react-hot-toast";
import { Nav, NotFoundPage } from "ui";
import { Footer, Nav, NotFoundPage } from "ui";
import ProtectRoute from "./components/ProtectRoute";
import { SignupPage } from "./components/SignupPage";
import LoginPage from "./components/LoginPage";
Expand Down Expand Up @@ -31,6 +31,7 @@ const App: React.FC = () => {
</Route>
<Route path="*" element={<NotFoundPage />}/>
</Routes>
<Footer/ >
</Router>
</>
)
Expand Down
31 changes: 30 additions & 1 deletion apps/user/src/components/Landing.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { useRecoilValue } from 'recoil'
import { allProd } from '../store/atom'
import { ProdCard } from 'ui'

const Landing: React.FC = () => {

const allProds = useRecoilValue(allProd);
const featuredProducts = [allProds[5], allProds[6], allProds[2]];

return (
<div>Landin</div>
<div className="bg-gray-100">


{/* Hero Section */}
<section className="bg-blue-600 text-white py-20">
<div className="container mx-auto text-center">
<h1 className="text-4xl font-bold mb-4">Discover Amazing Products</h1>
<p className="text-lg mb-8">Shop the latest trends in our wide selection of products.</p>
<Link to={'/user/prods'} className="bg-white text-blue-600 hover:bg-blue-600 hover:text-white py-2 px-6 rounded-full text-lg font-semibold transition duration-300">Shop Now</Link>
</div>
</section>

{/* Featured Products Section */}
<section className="py-16">
<h2 className="text-2xl font-medium tracking-wide font-sans ml-[87px]">Featured Products</h2>
<div className="container mx-auto flex flex-wrap gap-4 items-center justify-center">
{featuredProducts.map(fp => <ProdCard product={fp} client='user' key={fp._id} />)}
</div>
</section>

</div>

)
}

Expand Down
45 changes: 45 additions & 0 deletions apps/user/src/components/ProdPurchase.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,53 @@
import React from 'react'
import { legerType } from 'common'
import { LegerCard } from 'ui'
import { useRecoilState } from 'recoil'
import { allLegers } from '../store/atom'
import axios from 'axios'
import { baseURL } from './SignupPage'
import { toast } from 'react-hot-toast'
import { useNavigate } from 'react-router-dom'

export const ProdPurchase: React.FC = () => {

const navigate = useNavigate();
const [legers, setLegers] = useRecoilState(allLegers);

const handleDelete = (leger: legerType) => {
axios({
baseURL: baseURL,
url: `/user/purchased/${leger._id}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': sessionStorage.getItem('userToken')
}
}).then(response => {
const leg = legers.filter((l: legerType) => l._id != leger._id);
setLegers(leg);
toast.success(response.data.message);
return;
}).catch(err => {
if(err) {
if(err.status == 500) {
toast.error('Internal Server Error');
navigate('/user/login');
return;
}
toast.error(err.message);
// setLoader(false);
return;
}
})
}

return (
<>
<div className="min-h-screen py-10 flex items-center gap-10 flex-wrap justify-center bg-gray-100">
{legers &&
legers.map((leger: legerType) => <LegerCard leger={leger} client='user' deleteLeger={handleDelete} />)
}
</div>
</>
)
}
2 changes: 1 addition & 1 deletion apps/user/src/components/Prods.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const Prods: React.FC = () => {

return (
<>
{prods.state == 'hasValue' && <div className="min-h-screen flex items-center gap-10 flex-wrap justify-center bg-gray-100">
{prods.state == 'hasValue' && <div className="min-h-screen py-6 flex items-center gap-10 flex-wrap justify-center bg-gray-100">
{prods &&
prods.contents.map((prod: productType) =>
<ProdCard key={prod._id} product={prod} client='user' />
Expand Down
7 changes: 4 additions & 3 deletions apps/user/src/components/ProductPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { allLegers, allProd } from '../store/atom';
import { legerType, productType } from 'common';
import { legerInputType, legerType, productType } from 'common';
import { Product, PurchaseForm } from 'ui';
import axios from 'axios';
import { baseURL } from './SignupPage';
Expand All @@ -23,7 +23,7 @@ export const ProductPage: React.FC = () => {
}
}, []);

const handlePurchase = (leger: legerType) => {
const handlePurchase = (leger: legerInputType) => {
axios({
baseURL: baseURL,
url: `/user/prod/${prodId}`,
Expand All @@ -34,6 +34,7 @@ export const ProductPage: React.FC = () => {
'Authorization': sessionStorage.getItem('userToken')
}
}).then(response => {
console.log(response.data.leger)
setLeger((leg: legerType[]) => [...leg, response.data.leger]);
toast.success(response.data.message);
navigate('/user/prod/purchase')
Expand All @@ -44,7 +45,7 @@ export const ProductPage: React.FC = () => {
navigate('/user/login');
return;
}
console.log(err)
console.log(err, 'from here');
toast.error(err.message);
// setLoader(false);
return;
Expand Down
2 changes: 0 additions & 2 deletions apps/user/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import './index.css'
import { RecoilRoot } from 'recoil'

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RecoilRoot>
<App />
</RecoilRoot>
</React.StrictMode>,
)
2 changes: 1 addition & 1 deletion apps/user/src/store/atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const getlegers = selector({
get: async () => {
const response = await axios({
baseURL: baseURL,
url: '/user/prod/purchased',
url: '/user/purchased',
method: "GET",
headers: {
'Content-Type': 'application/json',
Expand Down
15 changes: 14 additions & 1 deletion packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,17 @@ export const legerInput = z.object({
phone: z.number(),
});

export type legerType = z.infer<typeof legerInput>;
export type legerInputType = z.infer<typeof legerInput>;

export const legerOutput = z.object({
product: productSignup,
quantity: z.number(),
address: z.string(),
phone: z.number(),
total: z.number(),
buyer: userSignupInput,
seller: adminSignupInput,
_id: z.string()
});

export type legerType = z.infer<typeof legerOutput>;
95 changes: 95 additions & 0 deletions packages/ui/src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React from 'react'
import { Link } from 'react-router-dom'

export const Footer: React.FC = () => {
return (


<footer className="bg-[#360CE3]">
<div className="mx-auto w-full max-w-screen-xl p-4 py-6 lg:py-8">
<div className="md:flex md:justify-between">
<div className="mb-6 md:mb-0">
<Link to="/" className="flex items-center">
<img src="https://flowbite.com/docs/images/logo.svg" className="h-8 mr-3" alt="FlowBite Logo" />
<span className="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">Vastram</span>
</Link>
</div>
<div className="grid grid-cols-2 gap-8 sm:gap-6 sm:grid-cols-3">
<div>
<h2 className="mb-6 text-sm font-semibold text-gray-900 uppercase dark:text-white">Resources</h2>
<ul className="text-gray-500 dark:text-gray-400 font-medium">
<li className="mb-4">
<Link to="/" className="hover:underline">Home</Link>
</li>
<li>
<Link to="/user/prods" className="hover:underline">Products</Link>
</li>
</ul>
</div>
<div>
<h2 className="mb-6 text-sm font-semibold text-gray-900 uppercase dark:text-white">Follow us</h2>
<ul className="text-gray-500 dark:text-gray-400 font-medium">
<li className="mb-4">
<Link to="https://github.com/shawakash" className="hover:underline ">Github</Link>
</li>
<li>
<Link to="https://discord.gg/4eeurUVvTy" className="hover:underline">Discord</Link>
</li>
</ul>
</div>
<div>
<h2 className="mb-6 text-sm font-semibold text-gray-900 uppercase dark:text-white">Legal</h2>
<ul className="text-gray-500 dark:text-gray-400 font-medium">
<li className="mb-4">
<Link to="#" className="hover:underline">Privacy Policy</Link>
</li>
<li>
<Link to="#" className="hover:underline">Terms &amp; Conditions</Link>
</li>
</ul>
</div>
</div>
</div>
<hr className="my-6 border-gray-200 sm:mx-auto dark:border-gray-700 lg:my-8" />
<div className="sm:flex sm:items-center sm:justify-between">
<span className="text-sm text-gray-500 sm:text-center dark:text-gray-400">© 2023 <Link to="/" className="hover:underline">Vastram™</Link>. All Rights Reserved.
</span>
<div className="flex mt-4 space-x-5 sm:justify-center sm:mt-0">
<Link to="#" className="text-gray-500 hover:text-gray-900 dark:hover:text-white">
<svg className="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 8 19">
<path fill-rule="evenodd" d="M6.135 3H8V0H6.135a4.147 4.147 0 0 0-4.142 4.142V6H0v3h2v9.938h3V9h2.021l.592-3H5V3.591A.6.6 0 0 1 5.592 3h.543Z" clip-rule="evenodd"/>
</svg>
<span className="sr-only">Facebook page</span>
</Link>
<Link to="#" className="text-gray-500 hover:text-gray-900 dark:hover:text-white">
<svg className="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 21 16">
<path d="M16.942 1.556a16.3 16.3 0 0 0-4.126-1.3 12.04 12.04 0 0 0-.529 1.1 15.175 15.175 0 0 0-4.573 0 11.585 11.585 0 0 0-.535-1.1 16.274 16.274 0 0 0-4.129 1.3A17.392 17.392 0 0 0 .182 13.218a15.785 15.785 0 0 0 4.963 2.521c.41-.564.773-1.16 1.084-1.785a10.63 10.63 0 0 1-1.706-.83c.143-.106.283-.217.418-.33a11.664 11.664 0 0 0 10.118 0c.137.113.277.224.418.33-.544.328-1.116.606-1.71.832a12.52 12.52 0 0 0 1.084 1.785 16.46 16.46 0 0 0 5.064-2.595 17.286 17.286 0 0 0-2.973-11.59ZM6.678 10.813a1.941 1.941 0 0 1-1.8-2.045 1.93 1.93 0 0 1 1.8-2.047 1.919 1.919 0 0 1 1.8 2.047 1.93 1.93 0 0 1-1.8 2.045Zm6.644 0a1.94 1.94 0 0 1-1.8-2.045 1.93 1.93 0 0 1 1.8-2.047 1.918 1.918 0 0 1 1.8 2.047 1.93 1.93 0 0 1-1.8 2.045Z"/>
</svg>
<span className="sr-only">Discord community</span>
</Link>
<Link to="#" className="text-gray-500 hover:text-gray-900 dark:hover:text-white">
<svg className="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 17">
<path fill-rule="evenodd" d="M20 1.892a8.178 8.178 0 0 1-2.355.635 4.074 4.074 0 0 0 1.8-2.235 8.344 8.344 0 0 1-2.605.98A4.13 4.13 0 0 0 13.85 0a4.068 4.068 0 0 0-4.1 4.038 4 4 0 0 0 .105.919A11.705 11.705 0 0 1 1.4.734a4.006 4.006 0 0 0 1.268 5.392 4.165 4.165 0 0 1-1.859-.5v.05A4.057 4.057 0 0 0 4.1 9.635a4.19 4.19 0 0 1-1.856.07 4.108 4.108 0 0 0 3.831 2.807A8.36 8.36 0 0 1 0 14.184 11.732 11.732 0 0 0 6.291 16 11.502 11.502 0 0 0 17.964 4.5c0-.177 0-.35-.012-.523A8.143 8.143 0 0 0 20 1.892Z" clip-rule="evenodd"/>
</svg>
<span className="sr-only">Twitter page</span>
</Link>
<Link to="#" className="text-gray-500 hover:text-gray-900 dark:hover:text-white">
<svg className="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 .333A9.911 9.911 0 0 0 6.866 19.65c.5.092.678-.215.678-.477 0-.237-.01-1.017-.014-1.845-2.757.6-3.338-1.169-3.338-1.169a2.627 2.627 0 0 0-1.1-1.451c-.9-.615.07-.6.07-.6a2.084 2.084 0 0 1 1.518 1.021 2.11 2.11 0 0 0 2.884.823c.044-.503.268-.973.63-1.325-2.2-.25-4.516-1.1-4.516-4.9A3.832 3.832 0 0 1 4.7 7.068a3.56 3.56 0 0 1 .095-2.623s.832-.266 2.726 1.016a9.409 9.409 0 0 1 4.962 0c1.89-1.282 2.717-1.016 2.717-1.016.366.83.402 1.768.1 2.623a3.827 3.827 0 0 1 1.02 2.659c0 3.807-2.319 4.644-4.525 4.889a2.366 2.366 0 0 1 .673 1.834c0 1.326-.012 2.394-.012 2.72 0 .263.18.572.681.475A9.911 9.911 0 0 0 10 .333Z" clip-rule="evenodd"/>
</svg>
<span className="sr-only">GitHub account</span>
</Link>
<Link to="#" className="text-gray-500 hover:text-gray-900 dark:hover:text-white">
<svg className="w-4 h-4" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 0a10 10 0 1 0 10 10A10.009 10.009 0 0 0 10 0Zm6.613 4.614a8.523 8.523 0 0 1 1.93 5.32 20.094 20.094 0 0 0-5.949-.274c-.059-.149-.122-.292-.184-.441a23.879 23.879 0 0 0-.566-1.239 11.41 11.41 0 0 0 4.769-3.366ZM8 1.707a8.821 8.821 0 0 1 2-.238 8.5 8.5 0 0 1 5.664 2.152 9.608 9.608 0 0 1-4.476 3.087A45.758 45.758 0 0 0 8 1.707ZM1.642 8.262a8.57 8.57 0 0 1 4.73-5.981A53.998 53.998 0 0 1 9.54 7.222a32.078 32.078 0 0 1-7.9 1.04h.002Zm2.01 7.46a8.51 8.51 0 0 1-2.2-5.707v-.262a31.64 31.64 0 0 0 8.777-1.219c.243.477.477.964.692 1.449-.114.032-.227.067-.336.1a13.569 13.569 0 0 0-6.942 5.636l.009.003ZM10 18.556a8.508 8.508 0 0 1-5.243-1.8 11.717 11.717 0 0 1 6.7-5.332.509.509 0 0 1 .055-.02 35.65 35.65 0 0 1 1.819 6.476 8.476 8.476 0 0 1-3.331.676Zm4.772-1.462A37.232 37.232 0 0 0 13.113 11a12.513 12.513 0 0 1 5.321.364 8.56 8.56 0 0 1-3.66 5.73h-.002Z" clip-rule="evenodd"/>
</svg>
<span className="sr-only">Dribbble account</span>
</Link>
</div>
</div>
</div>
</footer>


)
}
41 changes: 41 additions & 0 deletions packages/ui/src/components/LegerCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { legerType, productType } from 'common'
import React, { useState } from 'react'

export const LegerCard: React.FC<{ leger: legerType, client: string, deleteLeger: (leger: legerType) => void }> = ({ leger, client = 'admin', deleteLeger }) => {

const product = leger.product;

const handleDelete = () => {
deleteLeger(leger);
}

return (
<>
<div className="bg-white rounded-lg w-[500px] shadow-md p-6 hover:shadow-2xl transition-all">
<div className="flex items-center w-56 justify-center mb-4">
<img
src={product.imageUrls[0]}
alt={product.title}
className=" object-contain"
/>
</div>
<h2 className="text-xl font-semibold mb-2">{product.title}</h2>
<p className="text-gray-600 mb-2 line-clamp-3">{product.description}</p>
<p className="text-lg font-semibold tracking-wide my-2 flex justify-between text-blue-600"><span className='text-gray-500'>Total: </span><span> ${leger.total} + <span className='text-gray-500'>GST: 18%</span></span></p>
<p className="text-gray-500 mb-2"><span className='font-semibold'>Quantity: </span>{leger.quantity}</p>
<div className="text-gray-500 mb-2 flex justify-between">
<p>Brand: {product.brand}</p>
<p>Category: {product.category}</p>
</div>
<p className="text-gray-500 mb-2 flex justify-between"><span className="text-gray-500 text-lg">To: </span>{leger.address}</p>
<p className="text-gray-500 mb-2 flex justify-between"><span className="text-gray-500 text-lg">Number: </span>*******{(leger.phone).toString().slice(7)}</p>

{client == 'user' && <button
onClick={handleDelete}
className="bg-blue-500 text-white py-2 px-4 rounded-md hover:bg-blue-600 duration-300 w-fit hover:scale-105 active:scale-95 transition-all">
Cancel Order
</button>}
</div>
</>
)
}
Loading

0 comments on commit 720997c

Please sign in to comment.