Skip to content

Commit

Permalink
Merge pull request #10 from abdulsamad/develop
Browse files Browse the repository at this point in the history
Completed  more features and Updated README
  • Loading branch information
abdulsamad authored Dec 17, 2023
2 parents dee1394 + 7850f43 commit 1db7e94
Show file tree
Hide file tree
Showing 18 changed files with 3,140 additions and 2,986 deletions.
25 changes: 13 additions & 12 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,25 @@
*.drawio text eol=lf

# git config
.gitattributes text
.gitignore text
.gitconfig text
.gitattributes text eol=lf
.gitignore text eol=lf
.gitconfig text eol=lf

# code analysis config
.eslintrc text
.prettierrc text
.env text
.env.example text
.eslintrc text eol=lf
.prettierrc text eol=lf
.env text eol=lf
.env.example text eol=lf
yarn.lock text eol=lf

# misc config
*.yaml text
*.yml text
.editorconfig text
*.yaml text eol=lf
*.yml text eol=lf
.editorconfig text eol=lf

# build config
*.npmignore text
*.bowerrc text
*.npmignore text eol=lf
*.bowerrc text eol=lf

# Heroku
Procfile text
Expand Down
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,27 @@

ShopperAve is a full-stack ecommerce web application built using MongoDB, Express, React, Node, and Turborepo. It features Swagger UI for API documentation and Cypress testing for the frontend, and Jest for the backend. With Shopper Ave, users can enjoy a seamless online shopping experience, from browsing products to placing orders, all within a secure and reliable platform. Its modern and user-friendly interface, combined with its powerful backend capabilities, make Shopper Ave a top choice for any ecommerce business looking to provide their customers with the best possible shopping experience.

### Technologies

- TurboRepo
- React
- Next.js
- TanStack Query
- Zustand
- Tailwind
- Zod
- React Hook Form
- Cypress
- Husky
- Jest
- Framer Motion
- Stripe
- Node
- Express
- MongoDB

<details>
<summary>Technologies</summary>
React, Next.js, TanStack Query, Zustand, Tailwind, Zod, React Hook Form, Cypress, Husky, Jest, Framer Motion, Stripe, Node, Express and MongoDB with TurboRepo
</details>
### Screenshots

## [![shopper ave screenshot](readme/shopper-ave-home.png 'Home')](#)

<!-- ### When will this be completed?

![I Don't know](https://media.giphy.com/media/cwTtbmUwzPqx2/giphy.gif "I don't know") -->
9 changes: 2 additions & 7 deletions apps/client/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
{
"root": true,
"extends": [
"custom",
"next",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended"
],
"plugins": ["react-hooks"],
"extends": ["custom", "next", "plugin:react/recommended", "plugin:jsx-a11y/recommended"],
"rules": {
"@next/next/no-html-link-for-pages": "off",
"react/jsx-uses-react": "off",
Expand Down
46 changes: 23 additions & 23 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,40 @@
"dependencies": {
"@dicebear/collection": "^5.3.4",
"@dicebear/core": "^5.3.4",
"@heroicons/react": "^2.0.16",
"@heroicons/react": "^2.0.18",
"@hookform/resolvers": "^2.9.11",
"@stripe/react-stripe-js": "^1.16.5",
"@stripe/stripe-js": "^1.48.0",
"@tanstack/react-query": "^4.24.10",
"@tanstack/react-query-devtools": "^4.26.1",
"axios": "^1.3.4",
"@stripe/stripe-js": "^1.54.2",
"@tanstack/react-query": "^4.32.6",
"@tanstack/react-query-devtools": "^4.32.6",
"axios": "^1.4.0",
"beautiful-react-hooks": "^3.12.2",
"dayjs": "^1.11.7",
"embla-carousel-autoplay": "^7.0.9",
"embla-carousel-react": "^7.0.9",
"framer-motion": "^10.0.1",
"next": "^13.2.3",
"dayjs": "^1.11.9",
"embla-carousel-autoplay": "^7.1.0",
"embla-carousel-react": "^7.1.0",
"framer-motion": "^10.15.1",
"next": "^13.4.13",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.3",
"zod": "^3.20.6",
"zustand": "^4.3.3"
"react-hook-form": "^7.45.4",
"zod": "^3.21.4",
"zustand": "^4.4.1"
},
"devDependencies": {
"@types/node": "18.14.5",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"autoprefixer": "^10.4.13",
"@types/node": "18.17.5",
"@types/react": "18.2.20",
"@types/react-dom": "18.2.7",
"autoprefixer": "^10.4.14",
"cssnano": "^5.1.15",
"cypress": "^12.7.0",
"eslint-config-next": "^13.2.3",
"cypress": "^12.17.3",
"eslint-config-next": "^13.4.13",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react": "^7.33.1",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.21",
"prettier-plugin-tailwindcss": "^0.2.4",
"postcss": "^8.4.27",
"prettier-plugin-tailwindcss": "^0.2.8",
"start-server-and-test": "^2.0.0",
"tailwindcss": "^3.2.7",
"tailwindcss": "^3.3.3",
"typescript": "^4.9.5"
}
}
29 changes: 27 additions & 2 deletions apps/client/pages/admin/manage-products/index.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
import React from 'react';
import type { NextPage } from 'next';
import type { NextPage, GetServerSideProps } from 'next';
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';

import { getProducts } from '@api/admin';
import AdminLayout from '@components/admin/layout';
import Product from '@components/admin/product';

const Index: NextPage = () => {
const { data, isLoading } = useQuery({ queryKey: ['orders'], queryFn: getProducts });

return (
<AdminLayout
isLoading={isLoading}
title={
<>
Manage <span className="text-primary">Products</span>
</>
}>
<h1 className="text-center">Coming soon...</h1>
<section className="space-y-5 py-4 text-center">
{data?.products.map((product) => (
<Product key={product._id} {...product} />
))}
</section>
</AdminLayout>
);
};

export const getServerSideProps: GetServerSideProps = async () => {
const queryClient = new QueryClient();

await queryClient.prefetchQuery({
queryKey: ['products'],
queryFn: getProducts,
});

return {
props: {
dehydratedState: dehydrate(queryClient),
},
};
};

export default Index;
2 changes: 1 addition & 1 deletion apps/client/pages/product/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useCart } from '@store/index';
import { getProduct } from '@api/user';
import Button from '@utils/Button';
import { formatCurrency } from '@utils/index';
import Photos from '@components/user/product/Photos';
import Photos from '@utils/Photos';
import Review from '@components/user/review';
import Stars from '@utils/Stars';

Expand Down
73 changes: 72 additions & 1 deletion apps/client/src/api/admin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Order, Product } from 'shared-types';
import { Order, Product, User } from 'shared-types';

import { axiosInstance } from './axiosInstance';

Expand All @@ -19,6 +19,22 @@ export const createProduct = async (details: FormData): Promise<createProductRes
return data;
};

export interface IDeleteProduct {
success: boolean;
product: Product;
}

export const deleteProduct = async (productId: string): Promise<IDeleteProduct> => {
const res = await axiosInstance.delete(`/admin/product/${productId}`, {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
const data = await res.data;
return data;
};

export interface ICreateCategoryRes {
success: boolean;
}
Expand All @@ -38,6 +54,22 @@ export const createCategory = async ({ name }: { name: string }): Promise<ICreat
return data;
};

export interface IGetProducts {
success: boolean;
products: Product[];
}

export const getProducts = async (): Promise<IGetProducts> => {
const res = await axiosInstance.get('/admin/products', {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
const data = await res.data;
return data;
};

export interface IGetOrders {
success: boolean;
orders: Order[];
Expand All @@ -53,3 +85,42 @@ export const getOrders = async (): Promise<IGetOrders> => {
const data = await res.data;
return data;
};

export interface IUpdateOrder {
success: boolean;
product: Order;
}

export const updateOrder = async (
orderId: string,
orderStatus: Order['orderStatus']
): Promise<IUpdateOrder> => {
const res = await axiosInstance.put(
`/admin/product/${orderId}`,
{ orderStatus },
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
}
);
const data = await res.data;
return data;
};

export interface IGetUsers {
success: boolean;
users: User[];
}

export const getUsers = async (): Promise<IGetUsers> => {
const res = await axiosInstance.get('/admin/users', {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
});
const data = await res.data;
return data;
};
13 changes: 11 additions & 2 deletions apps/client/src/components/admin/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,27 @@ import Sidebar from '@components/admin/sidebar';

interface Layout {
title: React.ReactNode | string;
isLoading?: boolean;
children: React.ReactNode;
}

const Layout = ({ title, children }: Layout) => {
const Layout = ({ title, isLoading, children }: Layout) => {
return (
<div className="grid max-h-[calc(100vh-124px)] flex-1 grid-cols-12 overflow-y-hidden">
<Sidebar />
<div className="col-span-10 overflow-auto">
<div className="container">
<h1 className="my-3 text-center text-4xl text-gray-700">{title}</h1>
</div>
{children}
<div className="h-[400px]">
{isLoading ? (
<div>
<h1>loading...</h1>
</div>
) : (
children
)}
</div>
</div>
</div>
);
Expand Down
Loading

0 comments on commit 1db7e94

Please sign in to comment.