Commit 838e5fb4 authored by Philippe Fonzin's avatar Philippe Fonzin

Merge branch 'fix/style' into 'master'

Fix/style

See merge request !22
parents 97c61015 b728ae5a
File deleted
...@@ -3,6 +3,7 @@ target/ ...@@ -3,6 +3,7 @@ target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/ !**/src/main/**/target/
!**/src/test/**/target/ !**/src/test/**/target/
.DS_Store
### STS ### ### STS ###
.apt_generated .apt_generated
......
...@@ -17,27 +17,42 @@ ...@@ -17,27 +17,42 @@
--- ---
## Details ## Details
#### Schema ### Schemas
#### Warehouse Order
``` ```
{ {
_id: String id: String,
orderId: String orderId: String,
status: String "unfulfilled" (default) > "fulfilled"/"cancelled" status: String "RECEIVED" (default) > "FULFILLED"/"CANCELLED",
orderObject?: (will have it on initial Kafka message, not sure if we need to store this) createdAt: Date,
modifiedAt: Date,
orderItems: List<Item>,
address: String,
} }
``` ```
#### Workflow #### Item
- Warehouse Management (WM) expects an Order object (?) from Order Management (OM) on order placement in Kafka. ```
- On receipt of an Order object, WM will create a warehouse order entry in database with a status of "unfulfilled." {
itemId: String,
itemName: String,
itemQuantity: int,
itemPrice: float,
itemSku: int,
}
```
### Workflow
- Warehouse Management (WM) expects an Order object from Order Management (OM) on order creation in Kafka.
- On receipt of an Order object, WM will create a warehouse order entry in database with a status of **"RECEIVED"**.
- In the WM UI, a warehouse manager will have the ability to fulfill or cancel unfulfilled orders. - In the WM UI, a warehouse manager will have the ability to fulfill or cancel unfulfilled orders.
- When an order is marked **"fulfilled"** or **"cancelled"**, a Kafka message will be sent to be consumed. - When an order is marked **"FULFILLED"** or **"CANCELLED"**, a Kafka message will be sent to be consumed.
#### UI ### UI
- Login - Login/Logout
- Order status update screen mark orders as fulfilled or cancelled - Order Status and Update orders as **"FULFILLED"** or **"CANCELLED"**
- Order search - Order Filter and Search
- Order information page - Order Details
#### API Documentation #### API Documentation
https://documenter.getpostman.com/view/7402212/TzRNGATe https://documenter.getpostman.com/view/7402212/TzRNGATe
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
content="Web site created using create-react-app" content="Web site created using create-react-app"
/> />
<link rel="stylesheet" href="./stylesheets/master.css"> <link rel="stylesheet" href="./stylesheets/master.css">
<link href='http://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
......
...@@ -49,11 +49,11 @@ ...@@ -49,11 +49,11 @@
} }
.filter-all:hover { .filter-all:hover {
background: #2b4162; background: #00567D;
color: white; color: white;
} }
.filter-all.selected { .filter-all.selected {
background: #2b4162; background: #00567D;
} }
.filter-rec:hover { .filter-rec:hover {
background: #2292A4; background: #2292A4;
...@@ -90,20 +90,20 @@ ...@@ -90,20 +90,20 @@
cursor: pointer; cursor: pointer;
padding: 10px 0; padding: 10px 0;
font-size: 16px; font-size: 16px;
font-family: "Times New Roman", Times, serif; font-family: 'Lato', sans-serif;
} }
.search > input { .search > input {
padding: 10px; padding: 10px;
font-size: 16px; font-size: 16px;
font-family: "Times New Roman", Times, serif; font-family: 'Lato', sans-serif;
} }
.search-btn:hover, .search-btn:hover,
.collapse-btn:hover, .collapse-btn:hover,
.expand-btn:hover { .expand-btn:hover {
color: white; color: white;
background: #2b4162; background: #00567D;
} }
.search-select-error, .search-select-error,
......
...@@ -2,21 +2,90 @@ ...@@ -2,21 +2,90 @@
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
position: relative;
padding: 10px 20px; padding: 10px 20px;
height: 50px; height: 50px;
background: #2b4162; background: #00567D;
color: #fff;; color: #EBEBEB;
font-size: 28px; font-size: 28px;
font-weight: 700; font-weight: 700;
} }
.nisum-logo {
margin-right: 10px;
width: 100px;
height: 50px;
}
.header-module {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
}
.header > div { .header > div {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.header img { .user {
cursor: pointer;
position: relative;
display: flex;
align-items: center;
font-size: 20px;
padding: 10px;
}
.user:hover {
color: #CCCDCF;
}
.user img {
margin-right: 10px; margin-right: 10px;
width: 32px; width: 32px;
height: 32px; width: 32px;
border-radius: 50%;
}
.dropdown {
cursor: auto;
width: 100%;
position: absolute;
display: flex;
flex-direction: column;
background: white;
color: black;
padding: 10px;
border-radius: 4px;;
border: 1px solid black;
font-size: 16px;
font-weight: 400;
top: 30px;
right: 10px;
}
.dropdown > * ~ * {
margin-top: 10px;
}
.dropdown > * {
padding: 5px 10px;
}
.dropdown > *:hover {
background: #e9ecef;
}
.logout {
cursor: pointer;
}
.logout-btn {
border: none;
} }
\ No newline at end of file
...@@ -5,10 +5,15 @@ ...@@ -5,10 +5,15 @@
@import './order.css'; @import './order.css';
@import './session.css'; @import './session.css';
/* background: #2b4162; */ /* background: #00567D; */
/* background: #4daa57 */ /* background: #4daa57 */
/* background: #c1292e */ /* background: #c1292e */
/* background: #2292A4; */ /* background: #2292A4; */
/* background: #f5f0f6; */ /* background: #f5f0f6; */
/* background: #acb0bd; */ /* background: #acb0bd; */
body {
font-family: 'Lato', sans-serif;
}
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.order-index > h1 { .order-index > h1 {
padding: 10px 20px; padding: 10px 20px;
margin-bottom: 2px; margin-bottom: 2px;
background: #2b4162; background: #00567D;
color: white; color: white;
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
......
const Image = ({ src }) => ( const Image = ({ className, src }) => (
<img src={src} alt=""/> <img className={className} src={src} alt=""/>
) )
export default Image; export default Image;
\ No newline at end of file
...@@ -10,7 +10,10 @@ const Search = ({ orders, setOrdersToShow, setFiltersOn }) => { ...@@ -10,7 +10,10 @@ const Search = ({ orders, setOrdersToShow, setFiltersOn }) => {
const [inputError, setInputError] = useState(false); const [inputError, setInputError] = useState(false);
const [byError, setByError] = useState(false); const [byError, setByError] = useState(false);
const searchOptions = ["Warehouse ID", "Order ID"]; const searchOptions = [
// "Warehouse ID",
"Order ID"
];
const search = () => { const search = () => {
setInputError(false); setInputError(false);
...@@ -24,17 +27,13 @@ const Search = ({ orders, setOrdersToShow, setFiltersOn }) => { ...@@ -24,17 +27,13 @@ const Search = ({ orders, setOrdersToShow, setFiltersOn }) => {
setByError(true); setByError(true);
} else { } else {
const searchResult = { allIds: [], byId: {} }; const searchResult = { allIds: [], byId: {} };
const searchedOrder = orders.allIds.map(id => {
searchBy === searchOptions[0] const order = orders.byId[id];
? orders.byId[searchInput] if (order.orderId.toLowerCase().includes(searchInput.toLowerCase())) {
: searchBy === searchOptions[1] searchResult.allIds.push(id);
? orders.byOrderId[searchInput] searchResult.byId[id] = orders.byId[id];
: null;
if (searchedOrder) {
searchResult.allIds.push(searchedOrder.id);
searchResult.byId[searchedOrder.id] = searchedOrder;
} }
})
if (searchBy.length) { if (searchBy.length) {
setOrdersToShow(searchResult); setOrdersToShow(searchResult);
} }
......
import Logout from "../session/Logout"
const Dropdown = ({ }) => {
return (
<div className="dropdown">
<div>Profile</div>
<Logout />
</div>
)
}
export default Dropdown;
\ No newline at end of file
import { connect } from "react-redux"; import { connect } from "react-redux";
import Image from "../atoms/Image"; import Image from "../atoms/Image";
import Logout from "../session/Logout"; import Logout from "../session/Logout";
import User from "./User";
const Header = ({ isLoggedIn, logout }) => { const Header = ({ isLoggedIn, user, logout }) => {
return ( return (
<div className="header"> <div className="header">
<div> <div>
<Image src="./logo.svg" /> <Image className="nisum-logo" src="./nisum.jpeg" />
</div>
{/* <div>Ascend</div> */}
<div className="header-module">
<div>Warehouse Management</div> <div>Warehouse Management</div>
</div> </div>
{isLoggedIn ? <Logout onClick={logout} /> : null} <div>{isLoggedIn ? <User user={user.user} /> : null}</div>
</div> </div>
); );
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
isLoggedIn: state.session.isAuthenticated, isLoggedIn: state.session.isAuthenticated,
user: state.session.user,
}); });
const mapDispatchToProps = (dispatch) => ({}); const mapDispatchToProps = (dispatch) => ({});
......
import { useState } from "react";
import Image from "../atoms/Image";
import Dropdown from "./Dropdown";
const User = ({ user }) => {
const [showDropdown, setShowDropdown] = useState(false);
return (
<div className="user" onClick={() => setShowDropdown(!showDropdown)}>
<Image src={user.imageUrl} />
<div>{user.givenName}</div>
{showDropdown ? <Dropdown /> : null}
</div>
);
}
export default User;
\ No newline at end of file
...@@ -5,19 +5,21 @@ import Button from "../atoms/Button"; ...@@ -5,19 +5,21 @@ import Button from "../atoms/Button";
const OrderButtons = ({ order, editOrder }) => { const OrderButtons = ({ order, editOrder }) => {
const FULFILLED = "FULFILLED";
const CANCELLED = "CANCELLED";
const handleUpdate = (action) => { const handleUpdate = (action) => {
console.log(action); if (action === FULFILLED) {
if (action === "FULFILL") { editOrder({ ...order, status: FULFILLED });
editOrder({ ...order, status: "FULFILLED" }); } else if (action === CANCELLED) {
} else if (action === "CANCEL") { editOrder({ ...order, status: CANCELLED });
editOrder({ ...order, status: "CANCELLED" });
} }
}; };
return ( return (
<div className="oii-buttons"> <div className="oii-buttons">
<Button className="fulfill-btn" onClick={() => handleUpdate("FULFILL")} text="Fulfill"/> <Button className="fulfill-btn" onClick={() => handleUpdate(FULFILLED)} text="Fulfill"/>
<Button className="cancel-btn" onClick={() => handleUpdate("CANCEL")} text="Cancel"/> <Button className="cancel-btn" onClick={() => handleUpdate(CANCELLED)} text="Cancel"/>
</div> </div>
); );
}; };
......
...@@ -50,7 +50,7 @@ const OrderDetails = ({ order, showDetails }) => { ...@@ -50,7 +50,7 @@ const OrderDetails = ({ order, showDetails }) => {
))} ))}
</tbody> </tbody>
</table> </table>
{`Warehouse Order #: ${order.id}`} {/* {`Warehouse Order #: ${order.id}`} */}
</div> </div>
</div> </div>
); );
......
...@@ -11,6 +11,7 @@ const OrderIndexItem = ({ ...@@ -11,6 +11,7 @@ const OrderIndexItem = ({
setExpandAll, setExpandAll,
}) => { }) => {
const { orderId, status } = order; const { orderId, status } = order;
const idToShow = orderId.slice(-7).toUpperCase();
const [showDetails, setShowDetails] = useState(false); const [showDetails, setShowDetails] = useState(false);
...@@ -44,7 +45,7 @@ const OrderIndexItem = ({ ...@@ -44,7 +45,7 @@ const OrderIndexItem = ({
className={`oii-drop ${showDetails ? "rotate" : ""}`} className={`oii-drop ${showDetails ? "rotate" : ""}`}
onClick={handleDropDown} onClick={handleDropDown}
/> />
<div className="oii-num">{`Order #: ${orderId}`}</div> <div className="oii-num">{`Order #: ${idToShow}`}</div>
</div> </div>
{actions} {actions}
</div> </div>
......
...@@ -11,11 +11,14 @@ const Logout = ({ logout }) => { ...@@ -11,11 +11,14 @@ const Logout = ({ logout }) => {
}; };
return ( return (
<div> <div className="logout">
{/* Logout */}
<GoogleLogout <GoogleLogout
clientId={clientId} clientId={clientId}
buttonText="Logout" // buttonText="Logout"
className="logout-btn"
onLogoutSuccess={responseGoogle} onLogoutSuccess={responseGoogle}
render={props => (<div onClick={props.onClick}>Logout</div>)}
/> />
</div> </div>
); );
......
...@@ -17,12 +17,10 @@ const OrdersReducer = (oldState = initialState, action) => { ...@@ -17,12 +17,10 @@ const OrdersReducer = (oldState = initialState, action) => {
const orderState = { const orderState = {
byId: {}, byId: {},
allIds: [], allIds: [],
byOrderId: {},
}; };
action.orders.forEach( order => { action.orders.forEach( order => {
orderState.allIds.push(order.id); orderState.allIds.push(order.id);
orderState.byId[order.id] = order; orderState.byId[order.id] = order;
orderState.byOrderId[order.orderId] = order;
}) })
return orderState; return orderState;
case UPDATE_ORDER: case UPDATE_ORDER:
......
...@@ -9,15 +9,14 @@ import lombok.ToString; ...@@ -9,15 +9,14 @@ import lombok.ToString;
@ToString @ToString
public class Address { public class Address {
public Address() {
}
private String street; private String street;
private String city; private String city;
private String state; private String state;
private String zip; private String zip;
public Address() {
}
public Address(String street, String city, String state, String zip) { public Address(String street, String city, String state, String zip) {
this.street = street; this.street = street;
this.city = city; this.city = city;
......
...@@ -14,10 +14,13 @@ public class Item { ...@@ -14,10 +14,13 @@ public class Item {
private String itemId; private String itemId;
private String itemName; private String itemName;
private int itemQuantity; private int itemQuantity;
private double itemPrice; private float itemPrice;
private int itemSku; private String itemSku;
public Item(String itemId, String itemName, int itemQuantity, double itemPrice, int itemSku) { public Item() {
}
public Item(String itemId, String itemName, int itemQuantity, float itemPrice, String itemSku) {
this.itemId = itemId; this.itemId = itemId;
this.itemName = itemName; this.itemName = itemName;
this.itemQuantity = itemQuantity; this.itemQuantity = itemQuantity;
...@@ -25,7 +28,4 @@ public class Item { ...@@ -25,7 +28,4 @@ public class Item {
this.itemSku = itemSku; this.itemSku = itemSku;
} }
public Item() {
}
} }
...@@ -8,6 +8,7 @@ import org.springframework.data.annotation.Id; ...@@ -8,6 +8,7 @@ import org.springframework.data.annotation.Id;
@Setter @Setter
public class Session { public class Session {
@Id
private String token; private String token;
private User user;
} }
package com.ascendfinalproject.warehouse.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class User {
private String email;
private String familyName;
private String givenName;
private String googleId;
private String imageUrl;
private String name;
public User() {
}
}
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