Commit 1d610b9a authored by Shaphen Pangburn's avatar Shaphen Pangburn

[Ecom team]: Add frontend ecom-web file to new maven repo

parent d99ef97e
No preview for this file type
...@@ -20,7 +20,7 @@ public class ProductService { ...@@ -20,7 +20,7 @@ public class ProductService {
public Flux<Product> getAllProducts() { public Flux<Product> getAllProducts() {
return WebClient return WebClient
.builder() .builder()
.baseUrl("http://localhost:8081") .baseUrl("http://localhost:8083")
.build() .build()
.get() .get()
.uri("/api/products") .uri("/api/products")
......
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/ecom-web.iml" filepath="$PROJECT_DIR$/.idea/ecom-web.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
\ No newline at end of file
# Ecommerce
![Ecommerce Architecture Diagram](https://gitlab.mynisum.com/ascend/ecommerce/blob/master/ecom-web/src/resources/images/Ecommerce%20Architecture%20Diagram.jpeg)
## Tech Stack
- Back-end(API, Services, Databases):
- Java,
- SpringBoot,
- WebFlux
- WebClient
- MongoDB
- Front-end(Web UI/UX Development)
- React/Redux
- HTML/
## Schema
Field | Type | Description
----------- | --------- | ------------
Id | String | Generated from internal set of rules
email | Email | User email
firstName | String | User first name
lastName | String | User last name
password | String | User password
accessToken | String | Generated session token from Google SSO
cart | Array | List of products in the user's cart
createdAt | Datetime | Date and time of creation
### User Sample State
```json
User001: {
"id": TBD,
"email": "email@email.com",
"firstName": "Something",
"lastName": "Something",
"sessionToken": "flkjafu4p38joi34j1p541kjefghg",
"cart": [item1, item2, item3, item4]
}
```
\ No newline at end of file
This diff is collapsed.
{
"name": "nisum-ecommerce-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"animate.css": "^4.1.1",
"axios": "^0.21.1",
"jquery": "^3.6.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"redux": "^4.1.0",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- allow ajax calls -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<title>Ecommerce App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
import * as ApiUtil from '../util/product_api_util';
export const RECEIVE_ALL_PRODUCTS = "RECEIVE_ALL_PRODUCTS";
export const RECEIVE_ALL_PROMOTIONS = "RECEIVE_ALL_PROMOTIONS";
export const RECEIVE_ALL_PRODUCTS_AND_PROMOTIONS = "RECEIVE_ALL_PRODUCTS_AND_PROMOTIONS";
const receiveAllProducts = products => ({
type: RECEIVE_ALL_PRODUCTS,
products
})
const receiveAllPromotions = promotions => ({
type: RECEIVE_ALL_PROMOTIONS,
promotions
})
const receiveAllProductsAndPromotions = prodsAndPromos => ({
type: RECEIVE_ALL_PRODUCTS_AND_PROMOTIONS,
prodsAndPromos
})
export const fetchProducts = () => dispatch => ApiUtil.fetchProducts()
.then(products => dispatch(receiveAllProducts(products)));
export const fetchPromotions = () => dispatch => ApiUtil.fetchPromotions()
.then(promotions => dispatch(receiveAllPromotions(promotions)));
export const fetchProductsAndPromotions = () => dispatch => ApiUtil.fetchProductsAndPromotions()
.then(prodsAndPromos => dispatch(receiveAllProductsAndPromotions(prodsAndPromos)));
\ No newline at end of file
import React, {useState, useEffect} from 'react'
export default function PaymentMethod() {
return (
<div>
PAYMENT METHOD COMPONENT
</div>
)
}
import React, {useState, useEffect} from 'react'
export default function ReviewOrder() {
return (
<div>
REVIEW ORDER COMPONENT
</div>
)
}
import React, {useState, useEffect} from 'react'
export default function ShippingAddress() {
return (
<div>
SHIPPING ADDRESS COMPONENT
</div>
)
}
import React, {useState, useEffect} from 'react'
export default function SubmitOrder() {
return (
<div>
SUBMIT ORDER COMPONENT
</div>
)
}
import { connect } from 'react-redux';
import Checkout from './checkout';
const mSTP = state => ({
});
const mDTP = dispatch => ({
});
export default connect(mSTP, mDTP)(Checkout);
\ No newline at end of file
import React, {useState, useEffect} from 'react'
import ShippingAddress from './ShippingAddress.js'
import PaymentMethod from './PaymentMethod.js'
import ReviewOrder from './ReviewOrder.js'
import SubmitOrder from './SubmitOrder.js'
export default function checkout() {
return (
<div>
</div>
)
}
/*
1. Shipping Address
2. Payment Method
3. Review Order
4. Submit Order
*/
\ No newline at end of file
import { connect } from 'react-redux';
import OrderHistory from './order-history';
const mSTP = state => ({
});
const mDTP = dispatch => ({
});
export default connect(mSTP, mDTP)(OrderHistory);
\ No newline at end of file
import React, { Component } from 'react'
export default class OrderHistory extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
This is the OrderHistory component
</div>
)
}
}
\ No newline at end of file
import { connect } from 'react-redux';
import ProductMarket from './product-market';
import { fetchProducts, fetchPromotions } from '../../actions/product_actions';
const mSTP = state => ({
products: state.market.products,
promotions: state.market.promotions,
});
const mDTP = dispatch => ({
getProducts: () => dispatch(fetchProducts()),
getPromotions: () => dispatch(fetchPromotions())
});
export default connect(mSTP, mDTP)(ProductMarket);
\ No newline at end of file
import '../../resources/stylesheets/product-market.css';
import React, { Component } from 'react'
export default class ProductMarket extends Component {
constructor(props) {
super(props)
this.state = {}
}
componentDidMount() {
this.props.getProducts();
this.props.getPromotions();
}
render() {
return (
<div>
{this.props.products.map(prod => {
return (
<div key={ prod.sku }>
<p>{ prod.productName }</p>
<p>{ prod.productDescription }</p>
<p>${ prod.price }</p>
<button>Add to Cart</button>
<p>_________________________________</p>
</div>
)
})}
</div>
)
}
}
import React from 'react';
import { Provider } from 'react-redux'
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';
import SessionContainer from './session/session-container';
import ProductMarketContainer from './product-market/product-market-container';
import ShoppingCartContainer from './shopping-cart/shopping-cart-container';
import CheckoutContianer from './checkout/checkout-container';
import OrderHistoryContainer from './order-history/order-history';
const Root = ({ store }) => (
<Provider store={ store }>
<BrowserRouter>
<Switch>
<Route
exact path="/"
render={() => {
return (
// this.state.isUserAuthenticated ? // This can be changed for however our frontend user auth will operate
// <Redirect to="/product-market" /> :
<Redirect to="/product-market" />
)
}}
/>
<Route path="/session" component={ SessionContainer } /> { /* this can be removed if never used */ }
<Route path="/product-market" component={ ProductMarketContainer } />
<Route path="/cart" component={ ShoppingCartContainer } />
<Route path="/checkout" component={ CheckoutContianer } />
<Route path="/orders" component={ OrderHistoryContainer } />
</Switch>
</BrowserRouter>
</Provider>
)
export default Root;
\ No newline at end of file
import { connect } from 'react-redux';
import Session from './session';
const mSTP = state => ({
});
const mDTP = dispatch => ({
});
export default connect(mSTP, mDTP)(Session);
\ No newline at end of file
import React, { Component } from 'react'
export default class Session extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
<div>
This is the Session component
</div>
)
}
}
import React from 'react'
import './shopping-cart.css'
export default function CartItem(props) {
return (
<div className="shoppingCartItem" >
{console.log(props.productInfo)}
{/* This is based on amazon's style */}
{/* Flex-Direction: Row */}
{/* Left image div */}
<div id="productImageContainer">
<img className="productImage" src={props.productInfo.productImageUrl} />
</div>
{/* Name and Product details */}
<div id="nameAndDetails">
<div id="productName">{props.productInfo.productName}</div>
<div id="productStock">{props.productInfo.availableStock} left in stock</div>
<div id="quantityControls">
<select id="productQuantitySelect"><option>{props.productInfo.quantity}</option></select>
<button>Delete</button>
</div>
</div>
{/* Price and promotions */}
<div id="priceAndPromotions">
<div id="productPrice">${props.productInfo.price.toFixed(2)}</div>
</div>
</div>
)
}
/*
brand
name
price
*/
\ No newline at end of file
import { connect } from 'react-redux';
import ShoppingCart from './shopping-cart';
const mSTP = state => ({
});
const mDTP = dispatch => ({
});
export default connect(mSTP, mDTP)(ShoppingCart);
\ No newline at end of file
* {
font-family: Arial, sans-serif;
}
#shoppingCartContainer {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
height: 100vh;
width: 100%;
}
.shoppingCartItem {
width: 80%;
min-height: 250px;
border-top: 2px solid black;
display: flex;
background-color: white;
color: black;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
#productImageContainer {
display: flex;
width: 30%;
margin-right: 20px;
max-height: 100%;
max-width: 100%;
}
.productImage {
display: flex;
width: 100%;
/* height: auto; */
/* max-height: 100%; */
}
#nameAndDetails {
display: flex;
width: 45%;
height: 200px;
flex-direction: column;
text-align: left;
}
#priceAndPromotions {
display: flex;
width: 10%;
height: 200px;
/* background-color: green; */
flex-direction: column;
align-items: flex-end;
}
#productQuantitySelect {
width: 40px;
}
import React, {useState, useEffect} from 'react'
import CartItem from './CartItem.js'
import './shopping-cart.css'
export default function ShoppingCart() {
const [cartItems, setCartItems] = useState([])
// load the json containing user's cart items
useEffect( () => {
setCartItems(
[
{
"id": 1,
"sku": 1,
"upc": 1,
"productName": "UMYOGO Mens Athletic Walking Blade Running Tennis Shoes",
"productDescription": "Rubber sole Rubber material of sole possesses high durability for prolonging the wearing time of our shoes.\nThe elastic blade soles have high flexibility which allows the shoes to bend strongly while doing sports.\nKnit upper material make it possible that your feet free breath when you run or walk.\nIt's soft and protective to cushion your every step",
"price": 45.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71djekdy4eL._AC_UY695_.jpg",
"brand": "UMYOGO",
"category": "shoe",
"quantity": 2
},
{
"id": 2,
"sku": 2,
"upc": 2,
"productName": "adidas Women's Cloudfoam Pure Running Shoe",
"productDescription": "100% Synthetic\nImported\nRubber sole\nPlatform measures approximately 0-3 inches\nBoot opening measures approximately 6-12 inches around\nWomen's specific fit\nCloudfoam memory sockliner and textile lining",
"price": 50.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71G0sB8FnFL._AC_UX695_.jpg",
"brand": "Adidas",
"category": "shoe"
},
{
"id": 3,
"sku": 3,
"upc": 3,
"productName": "Merrell Men's Moab 2 Vent Hiking Shoe",
"productDescription": "100% suede leather, mesh\nImported\nSynthetic sole\nPerformance suede leather and mesh upper\nBellows, closed-cell foam tongue keeps moisture and debris out",
"price": 100.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/81m9GyEmoRL._AC_UX695_.jpg",
"brand": "Merrell",
"category": "shoe"
},
{
"id": 4,
"sku": 4,
"upc": 4,
"productName": "Nike Women's Training Basketball Shoe",
"productDescription": "Rubber\nBrand New\n100% Authentic\nOriginal Packaging",
"price": 85.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71zKmTHcHUL._AC_UX695_.jpg",
"brand": "Nike",
"category": "shoe"
},
{
"id": 5,
"sku": 5,
"upc": 5,
"productName": "adidas Men's Daily 3.0 Skate Shoe",
"productDescription": "100% Leather\nRubber sole\nadidas Mens Skate Shoe\nThe adidas brand has a long history and deep-rooted connection with sport. Everything we do is rooted in sport",
"price": 85.00,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/71NVcBO0LEL._AC_UX695_.jpg",
"brand": "Adidas",
"category": "shoe"
},
{
"id": 6,
"sku": 6,
"upc": 6,
"productName": "Amazon Basics Lightweight Soft Easy Care Microfiber Pillowcases - 2-Pack, Black",
"productDescription": "100% Polyester\nImported\nIncludes two 20 x 30 inch standard-size pillowcases\nPolyester microfiber offers strength and exceptional softness",
"price": 8.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/818vTEo5TPL._AC_SX679_.jpg",
"brand": "Amazon Basics",
"category": "pillow case"
},
{
"id": 7,
"sku": 7,
"upc": 7,
"productName": "Pillowcase Set of 2 Durable Chair Cushion, Chair Pad Filled Cotton",
"productDescription": "Comfortable Thickness: Seat pad with a thickness of 10 cm is sufficient to relieve stress, so if you sit for a long time, you will feel its warmth and softness.",
"price": 42.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/61Txdww5uXS._AC_SX679_.jpg",
"brand": "PillowCase",
"category": "pillow case"
},
{
"id": 8,
"sku": 8,
"upc": 8,
"productName": "Amazon Basics Lightweight Soft Easy Care Microfiber Pillowcases - 2-Pack, Navy Blue",
"productDescription": "100% Polyester\nImported\nIncludes two 20 x 30 inch standard-size pillowcases\nPolyester microfiber offers strength and exceptional softness",
"price": 8.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/81jZRRyZCfL._AC_SX679_.jpg",
"brand": "Amazon Basics",
"category": "pillow case"
},
{
"id": 9,
"sku": 9,
"upc": 9,
"productName": "Bedsure Bamboo Pillow Cases Queen Set of 2 - Cooling Ultra Soft Pillowcases",
"productDescription": "Bamboo-derived Rayon\nExceptionally Bamboo Texture: Bedsure queen Pillowcases layer your sleep space with an irresistibly sleek sateen finish to transform your bed into a luxurious retreat - Slip in feathery soft pillowcases to have a dreamy rest as if you are sleeping on a cloud and also wake up with cool feelings - Silky smooth to the touch, bamboo pillowcase queen glides over your hair and face to create a beauty sleep.",
"price": 11.04,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/61IdiIz3qML._AC_SX679_.jpg",
"brand": "BedSure",
"category": "pillow case"
},
{
"id": 10,
"sku": 10,
"upc": 10,
"productName": "Amazon Basics Lightweight Soft Easy Care Microfiber Pillowcases - 2-Pack, Taupe",
"productDescription": "100% Polyester\nImported\nIncludes two 20 x 30 inch standard-size pillowcases\nPolyester microfiber offers strength and exceptional softness",
"price": 8.99,
"availableStock": 100,
"blockedStock": 20,
"productImageUrl": "https://images-na.ssl-images-amazon.com/images/I/91GB7gHxSuL._AC_SX679_.jpg",
"brand": "Amazon Basics",
"category": "pillow case"
}
]
)
}, [] )
return (
<div id="shoppingCartContainer">
{/* map each cart item into CartItem component */}
{cartItems.map( (currItem) => {
return (
<CartItem productInfo={currItem} />
)
})}
</div>
)
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
import React from 'react';
import ReactDOM from 'react-dom';
import configureStore from './store/store'
import Root from './components/root'
document.addEventListener("DOMContentLoaded", () => {
let store = configureStore();
const root = document.getElementById('root');
ReactDOM.render(<Root store={ store } />, root)
});
import { RECEIVE_ALL_PRODUCTS, RECEIVE_ALL_PROMOTIONS, RECEIVE_ALL_PRODUCTS_AND_PROMOTIONS } from '../actions/product_actions';
const initialState = {
products: [],
promotions: []
}
const productsReducer = (prevState = initialState, action) => {
Object.freeze(prevState);
const nextState = {...prevState};
switch (action.type) {
case RECEIVE_ALL_PRODUCTS:
nextState.products = action.products.data
return nextState;
case RECEIVE_ALL_PROMOTIONS:
nextState.promotions = action.promotions.data
return nextState;
case RECEIVE_ALL_PRODUCTS_AND_PROMOTIONS:
return action.prodsAndPromos;
default:
return nextState;
}
}
export default productsReducer;
\ No newline at end of file
import { combineReducers } from 'redux';
import productsReducer from './products_reducer';
const rootReducer = combineReducers({
market: productsReducer
});
export default rootReducer;
\ No newline at end of file
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../reducers/root_reducer';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
const configureStore = (preloadedState = {}) =>
createStore(rootReducer, preloadedState, applyMiddleware(thunk, logger))
export default configureStore;
\ No newline at end of file
import axios from 'axios';
export const fetchProducts = () => {
return axios.get("http://localhost:8080/api/products")
}
export const fetchPromotions = () => {
return axios.get("http://localhost:8080/api/promos")
}
export const fetchProductsAndPromotions = () => {
return axios.get("http://localhost:8080/api/products-and-promos")
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment