Commit 0b732f3a authored by John Lam's avatar John Lam

merge with AFP-110 dev and fix conflicts

parents 0ad17a5b 694b3040
...@@ -3,6 +3,27 @@ import axios from 'axios'; ...@@ -3,6 +3,27 @@ import axios from 'axios';
export const getAllProducts = async data => { export const getAllProducts = async data => {
const res = await axios.get(`${Config.inventoryUrl}`); const res = await axios.get(`${Config.inventoryUrl}`);
// console.log(res.data);
return res.data; return res.data;
}
export const deleteProduct = async (sku) => {
await axios.delete(`${Config.inventoryUrl}/${sku}`)
.then(() => {
getAllProducts();
});
}
export const getFilteredProducts = async (searchTerm, searchBy) => {
const res = await axios.get(`${Config.inventoryUrl}`);
return res.data.filter(product => {
let productFiltered;
if(searchBy === "name"){
productFiltered = product.productName.toLowerCase();
}else if(searchBy === "sku"){
productFiltered = product.sku.toLowerCase();
}
return productFiltered.includes(searchTerm.toLowerCase());
});
} }
\ No newline at end of file
import React, { useState } from "react"; import React, { useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import Search from "./Search";
const Header = () => { const Header = () => {
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
...@@ -38,16 +39,7 @@ const Header = () => { ...@@ -38,16 +39,7 @@ const Header = () => {
</Link> </Link>
</li> </li>
</ul> </ul>
<form className="d-flex"> <Search />
<input
type="search"
className="form-control me-2"
placeholder="search"
/>
<button className="btn btn-light" type="submit">
GO
</button>
</form>
</div> </div>
</div> </div>
</nav> </nav>
......
import React, { useState, useEffect } from 'react';
import { Redirect, Switch } from "react-router"; import { Redirect, Switch } from "react-router";
import AuthRoute from "./AuthRoute"; import AuthRoute from "./AuthRoute";
import ProductForm from "./ProductForm"; import ProductForm from "./ProductForm";
import PromotionNewFormComponent from './promotionforms/PromotionNewFormComponent' import ProductIndex from "./ProductIndex";
import ProductGrid from "./ProductGrid"; import SearchResults from "./SearchResults";
import {getAllProducts} from "../actions/apiRequests.js" import PromotionNewFormComponent from "./promotionforms/PromotionNewFormComponent";
import PromotionIndexComponent from './promo_index/PromotionsIndexComponent'; import PromotionIndexComponent from "./promo_index/PromotionsIndexComponent";
import PromotionUpdateFormComponent from './promotionforms/UpdatePromotionForm';
export default function Main() { export default function Main() {
const [productData, setproductData] = useState([]);
useEffect(() => {
loadProducts();
}, []);
const loadProducts = async (event) => {
const data = await getAllProducts();
setproductData(data);
}
return ( return (
<div> <div>
<Switch> <Switch>
<AuthRoute exact path="/products/new"> <AuthRoute exact path="/products/new">
<ProductForm /> <ProductForm method="POST" />
</AuthRoute> </AuthRoute>
<AuthRoute exact path="/promos/new" component={PromotionNewFormComponent}>NEW PROMO</AuthRoute> <AuthRoute exact path="/products/edit/:productId">
<AuthRoute exact path="/products"> <ProductForm method="PUT" />
<ProductGrid productData={productData} /> </AuthRoute>
<AuthRoute
exact
path="/promos/new"
component={PromotionNewFormComponent}
></AuthRoute>
<AuthRoute exact path="/products" component={ProductIndex}></AuthRoute>
<AuthRoute path="/promos">
<PromotionIndexComponent />
</AuthRoute> </AuthRoute>
<AuthRoute exact path="/promos/:promotionId/update" component={PromotionUpdateFormComponent}></AuthRoute>
<AuthRoute path="/promos" component={PromotionIndexComponent}>PROMOS</AuthRoute>
<AuthRoute exact path="/"> <AuthRoute exact path="/">
<Redirect to="/products" /> <Redirect to="/products" />
</AuthRoute> </AuthRoute>
<AuthRoute >404 PAGE</AuthRoute> <AuthRoute path="/searchResults" component={SearchResults} />
<AuthRoute>
<h1>404 Page Not Found</h1>
</AuthRoute>
</Switch> </Switch>
</div> </div>
); );
......
import React from 'react'
import './../styles/Product.css'
export default function Product({product}) {
return (
<div>
<div className="img-container">
<img src={product.productImageUrl} alt={product.productName}/>
</div>
<div className="prod-info">
<h5>{product.productName}</h5>
{product.sku}<br/>
${product.price}<br/>
In Stock: {product.stock}
</div>
</div>
)
}
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useHistory, useLocation } from "react-router"; import { useHistory, useParams } from "react-router";
import emptyProduct from "../emptProduct";
import Config from '../config';
const emptyForm = { const emptyForm = {
sku: "", ...emptyProduct,
upc: "", imageFile: "",
productName: "",
brand: "",
category: "",
price: 0.0,
availableStock: 0,
productDescription: "",
productImageUrl: "",
productImage: ""
}; };
ProductForm.defaultProps = { ProductForm.defaultProps = {
...@@ -19,15 +13,34 @@ ProductForm.defaultProps = { ...@@ -19,15 +13,34 @@ ProductForm.defaultProps = {
}; };
export default function ProductForm(props) { export default function ProductForm(props) {
const { product } = props; const { method } = props;
const [form, setForm] = useState(product); const [form, setForm] = useState({ ...emptyForm });
const [imageMode, setImageMode] = useState("url");
const [errors, setErrors] = useState([]); const [errors, setErrors] = useState([]);
const history = useHistory(); const history = useHistory();
const { productId } = useParams("productId");
useEffect(() => {
fetch(`http://localhost:8080/api/products/${productId}/`).then((res) => {
if (res.ok) {
console.log(res);
res.json().then((data) => {
Object.keys(data).forEach((key) => {
if (data[key] === null) {
data[key] = "";
}
});
console.log(data);
setForm({ ...data });
});
}
});
}, [productId]);
const validate = () => { const validate = () => {
setErrors([]); setErrors([]);
const errs = []; const errs = [];
console.log(form)
if (form.sku.length < 3) { if (form.sku.length < 3) {
errs.push("SKU must be at least 3 characters"); errs.push("SKU must be at least 3 characters");
} }
...@@ -56,11 +69,20 @@ export default function ProductForm(props) { ...@@ -56,11 +69,20 @@ export default function ProductForm(props) {
const onSubmit = (e) => { const onSubmit = (e) => {
e.preventDefault(); e.preventDefault();
validate(); validate();
if (imageMode === "url") {
delete form.imageFile;
} else {
delete form.prodImgUrl;
}
if (errors.length === 0) { if (errors.length === 0) {
// console.log(form); // console.log(form);
fetch("http://localhost:8080/api/products", { const url =
method: "POST", method === "PUT"
? `${Config.inventoryUrl}/${productId}`
: `${Config.inventoryUrl}`;
fetch(url, {
method,
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
...@@ -100,6 +122,7 @@ export default function ProductForm(props) { ...@@ -100,6 +122,7 @@ export default function ProductForm(props) {
SKU SKU
</label> </label>
<input <input
disabled={method === "PUT" ? true : false}
required required
name="sku" name="sku"
type="text" type="text"
...@@ -137,7 +160,9 @@ export default function ProductForm(props) { ...@@ -137,7 +160,9 @@ export default function ProductForm(props) {
className="form-control" className="form-control"
id="productName" id="productName"
value={form.productName} value={form.productName}
onChange={(e) => setForm({ ...form, productName: e.target.value })} onChange={(e) =>
setForm({ ...form, productName: e.target.value })
}
/> />
</div> </div>
<div> <div>
...@@ -170,17 +195,19 @@ export default function ProductForm(props) { ...@@ -170,17 +195,19 @@ export default function ProductForm(props) {
</div> </div>
</div> </div>
<div className="col-6"> <div className="col-6">
<label htmlFor="productDescription" className="form-label"> <label htmlFor="productDesc" className="form-label">
Description Description
</label> </label>
<textarea <textarea
name="productDescription" name="productDesciption"
id="productDescription" id="productDesc"
cols="40" cols="40"
rows="7" rows="7"
className="form-control" className="form-control"
value={form.productDescription} value={form.productDescription}
onChange={(e) => setForm({ ...form, productDescription: e.target.value })} onChange={(e) =>
setForm({ ...form, productDescription: e.target.value })
}
></textarea> ></textarea>
</div> </div>
</div> </div>
...@@ -204,8 +231,9 @@ export default function ProductForm(props) { ...@@ -204,8 +231,9 @@ export default function ProductForm(props) {
Stock Stock
</label> </label>
<input <input
disabled={method === "PUT" ? true : false}
required required
name="stock" name="availableStock"
type="number" type="number"
className="form-control" className="form-control"
id="stock" id="stock"
...@@ -214,28 +242,61 @@ export default function ProductForm(props) { ...@@ -214,28 +242,61 @@ export default function ProductForm(props) {
/> />
</div> </div>
</div> </div>
<div className="row mt-3"> {imageMode === "upload" ? (
<label htmlFor="productImage" className="form-label"> <div className="row mt-3">
Product Image <div>
</label> <label htmlFor="productImage" className="form-label">
<input Image File
id="productImageUrl" </label>
name="productImageUrl" <input
className="form-control form-control-lg" id="productImage"
type="url" name="imageFile"
placeholder="Enter image URL here or upload image below..." className="form-control form-control-lg"
value={form.productImageUrl} type="file"
onChange={(e) => setForm({ ...form, productImageUrl: e.target.value, productImage:""})} value={form.imageFile}
></input> onChange={(e) =>
<input setForm({ ...form, imageFile: e.target.value })
id="productImage" }
name="productImage" ></input>
className="form-control form-control-lg" <button
type="file" type="button"
value={form.productImage} className="btn btn-outline-primary mt-3"
onChange={(e) => setForm({ ...form, productImage: e.target.value, productImageUrl:"" })} onClick={() => {
></input> setForm({ ...form, imageFile: "" });
</div> setImageMode("url");
}}
>
Specify Image URL
</button>
</div>
</div>
) : null}
{imageMode === "url" ? (
<div className="row mt-3">
<div>
<label htmlFor="prodImgUrl" className="form-label">
Image URL
</label>
<input
name="productImageUrl"
type="text"
className="form-control"
id="prodImgUrl"
value={form.productImageUrl}
onChange={(e) =>
setForm({ ...form, productImageUrl: e.target.value })
}
/>
<button
type="button"
className="btn btn-outline-primary mt-3"
onClick={() => setImageMode("upload")}
>
Upload Image
</button>
</div>
</div>
) : null}
<div className="row mt-3"> <div className="row mt-3">
<div className="col-12"> <div className="col-12">
......
import React from "react";
import Product from "./Product.jsx";
import { Col, Container, Row } from "react-bootstrap";
import "./../styles/ProductGrid.css";
export default function ProductGrid({ productData }) {
return (
<div>
<h1 id="title" className="text-center" >Inventory</h1>
<Container id="prod-grid" className="mt-3">
<Row xs={1} sm={2} md={3} lg={4}>
{productData.map((product) => {
return (
<Col key={product.sku}>
<Product product={product} />
</Col>
);
})}
</Row>
</Container>
</div>
//uses vanilla bootstrap
// <div>
// <h1 id="title">Inventory</h1>
// <div className="container" id="prod-grid" >
// <div className="row row-cols-4">
// {productData.map((product) => {
// return (
// <div className="col" key={product.sku}>
// <Product product={product}/>
// </div>
// )
// })}
// </div>
// </div>
// </div>
);
}
import React, { useEffect, useState } from "react";
import ProductTable from "./ProductTable.jsx";
import Config from "../config.js";
import { Link } from "react-router-dom";
export default function ProductIndex() {
const [products, setProducts] = useState([]);
const [displayProducts, setDisplayProducts] = useState([]);
const [categories, setCategories] = useState([]);
const [activeCategory, setActiveCategory] = useState("");
console.log(displayProducts);
const fetchProducts = async () => {
const res = await fetch(`${Config.inventoryUrl}`);
if (res.ok) {
const data = await res.json();
setProducts([...data]);
setDisplayProducts([...data]);
setCategories([
...data.reduce((acc, prod) => {
if (!acc.includes(prod.category)) {
acc.push(prod.category);
}
return acc;
}, []),
]);
}
};
useEffect(() => fetchProducts(), []);
return (
<div className="container flex-column d-flex justify-content-center">
<div className="container mt-3 d-flex justify-content-between align-items-center">
<h1 id="title" className="text-center">
Inventory
</h1>
<Link type="link" className="btn btn-success" to="/products/new">
+ New Product
</Link>
</div>
{products.length > 0 ? (
<>
<select
className="form-select w-25 mt-1"
id="category-select"
onChange={(e) => {
if (e.target.value === "") {
setDisplayProducts([...products]);
return;
}
const filtered = products.filter(
(prod) => prod.category === e.target.value
);
setDisplayProducts([...filtered]);
}}
>
<option value="">Select Category</option>
{categories.map((category, i) => (
<option key={i + 679}>{category}</option>
))}
</select>
<ProductTable
productData={displayProducts}
fetchProducts={fetchProducts}
products={displayProducts}
/>
</>
) : (
<p>No products found.</p>
)}
</div>
);
}
import React, { useState } from "react";
import "./../styles/ProductRow.css";
import { Modal, Button, Alert } from "react-bootstrap";
export default function ProductRow({ product, handleDelete }) {
const [show, setShow] = useState(false);
const [showConfirm, setShowConfirm] = useState(false);
const handleClose = () => {
setShow(false);
console.log({ show });
handleCloseConfirm();
};
const handleCloseDelete = (sku) => {
handleDelete(sku);
handleClose();
};
const handleShow = () => setShow(true);
const handleShowConfirm = () => setShowConfirm(true);
const handleCloseConfirm = () => setShowConfirm(false);
return (
<tr>
<td onClick={handleShow}>{product.sku}</td>
<td onClick={handleShow}>{product.upc}</td>
<td onClick={handleShow}>{product.productName}</td>
<td onClick={handleShow}>${product.price}</td>
<td onClick={handleShow}>{product.category}</td>
<td onClick={handleShow}>{product.availableStock}</td>
<td onClick={handleShow}>{product.blockedStock}</td>
<td onClick={handleShow}>{product.fulfilledStock}</td>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>{product.productName}</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="modal-img">
<img src={product.productImageUrl} alt={product.productName} />
</div>
<h5>{product.sku}</h5>${product.price}
<br />
{product.productDescription}
<br />
In Stock: {product.availableStock}
<br />
Blocked Stock: {product.blockedStock}
<br />
Fulfilled Stock: {product.fulfilledStock}
</Modal.Body>
<Modal.Footer>
<Button
variant="danger"
className="float-left"
onClick={handleShowConfirm}
>
Delete product
</Button>
<Button variant="primary" href={`/products/edit/${product.sku}`}>
Edit Product
</Button>
<Alert show={showConfirm} variant="danger">
<h5> Are you sure?</h5>
<Button variant="secondary" onClick={handleCloseConfirm}>
Cancel
</Button>
&nbsp;&nbsp;
<Button
variant="danger"
onClick={() => handleCloseDelete(product.sku)}
>
Yes, delete
</Button>
</Alert>
</Modal.Footer>
</Modal>
</tr>
);
}
import React, { useState, useEffect } from "react";
import ProductRow from "./ProductRow.jsx";
import { Container, Table } from "react-bootstrap";
import Config from "../config.js";
import { deleteProduct } from "../actions/apiRequests";
export default function ProductTable({ fetchProducts, products }) {
// const [products, setProducts] = useState([...productData]);
// const fetchProducts = async () => {
// const res = await fetch(`${Config.inventoryUrl}`);
// if (res.ok) {
// const data = await res.json();
// setProducts([...data]);
// }
// };
const handleDelete = (sku) => {
deleteProduct(sku)
.then(() => {
fetchProducts();
});
}
useEffect(() => fetchProducts(), []);
return (
<Container id="prod-table" className="mt-3 mx-auto">
<Table>
<thead>
<tr>
<th>SKU</th>
<th>UPC</th>
<th>Product Name</th>
<th>Price</th>
<th>Category</th>
<th>Available Stock</th>
<th>Blocked Stock</th>
<th>Fulfilled Stock</th>
</tr>
</thead>
<tbody>
{products.map((product) => {
return (
<ProductRow key={product.sku} product={product} handleDelete={() => handleDelete(product.sku)} />
);
})}
</tbody>
</Table>
</Container>
);
}
import React from 'react';
import { withRouter, Link } from "react-router-dom";
import { getFilteredProducts } from '../actions/apiRequests';
import "./../styles/Search.css";
class Search extends React.Component {
constructor(props){
super(props);
this.state = {
searchTerm: '',
searchBy: 'name',
results: [],
};
this.submit = this.submit.bind(this);
this.changeTerm = this.changeTerm.bind(this);
this.handleRadioButton = this.handleRadioButton.bind(this);
}
changeTerm(event) {
this.setState({searchTerm: event.target.value});
}
handleRadioButton(value) {
this.setState({
searchBy: value
});
}
async submit(event){
event.preventDefault();
const data = await getFilteredProducts(this.state.searchTerm, this.state.searchBy)
.then(res => {
this.setState({results: res});
})
.catch(err => console.log(err));
if(this.props.location.pathname === "/searchResults"){
this.props.history.push("/products", this.state);
}
this.props.history.push("/searchResults", this.state);
}
//Need to search by name or SKU
render(){
return (
<div>
<form className="d-flex" onSubmit={this.submit}>
<input
type="search"
className="form-control me-2"
placeholder="Search for item..."
onChange={event => this.changeTerm(event)}
/>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="inlineRadioOptions" checked={this.state.searchBy === "name"} onChange={() => this.handleRadioButton("name")} id="searchByName" value="name" />
<label className="form-check-label" htmlFor="searchByName">Name</label>
</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="inlineRadioOptions" checked={this.state.searchBy === "sku"} onChange={() => this.handleRadioButton("sku")} id="searchBySku" value="sku" />
<label className="form-check-label" htmlFor="searchBySKU">Sku</label>
</div>
<button className="btn btn-light" type="submit">
GO
</button>
</form>
<Link to={{
pathname: '/searchResults',
state: {results: this.state.results}
}} />
</div>
);
}
}
export default withRouter(Search);
\ No newline at end of file
import React from "react";
import ProductRow from "./ProductRow.jsx";
import { Table } from "react-bootstrap";
import { deleteProduct } from "../actions/apiRequests";
import { withRouter } from "react-router";
class SearchResults extends React.Component {
constructor(props) {
super(props);
this.state = {
results: this.props.history.location.state.results
}
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(sku){
deleteProduct(sku)
.then(() => {
const newResults = this.state.results.filter(product => product.sku !== sku);
this.setState({results: newResults});
this.props.history.push("/searchResults", this.state);
});
}
render() {
return (
<div className="container flex-column d-flex justify-content-center">
<h1 id="title" className="text-center" >Search Results</h1>
{this.state.results.length > 0 ?
<Table>
<thead>
<tr>
<th>SKU</th>
<th>Product Name</th>
<th>Price</th>
<th>Category</th>
<th>Available Stock</th>
<th>Blocked Stock</th>
<th>Fulfilled Stock</th>
</tr>
</thead>
<tbody>
{this.state.results.map((product) => {
return (
<ProductRow key={product.sku} product={product} handleDelete={() => this.handleDelete(product.sku)} />
);
})}
</tbody>
</Table>
:
<p>Unable to find any matching products.</p>
}
</div>
);
}
}
export default withRouter(SearchResults);
\ No newline at end of file
...@@ -3,11 +3,11 @@ import Config from '../../config'; ...@@ -3,11 +3,11 @@ import Config from '../../config';
import "./promolistStyle.css"; import "./promolistStyle.css";
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
export default function PromotionIndexComponent ({}) { export default function PromotionIndexComponent () {
const [promoData, setPromoData] = useState([]); const [promoData, setPromoData] = useState([]);
useEffect(async () => { useEffect( () => {
loadPromotions(); loadPromotions();
}, []) }, [])
...@@ -15,6 +15,7 @@ export default function PromotionIndexComponent ({}) { ...@@ -15,6 +15,7 @@ export default function PromotionIndexComponent ({}) {
const response = await fetch(`${Config.promotionsUrl}`); const response = await fetch(`${Config.promotionsUrl}`);
const data = await response.json(); const data = await response.json();
setPromoData(data); setPromoData(data);
console.log(data);
} }
const deletePromotion = (promoId) => { const deletePromotion = (promoId) => {
...@@ -28,7 +29,6 @@ export default function PromotionIndexComponent ({}) { ...@@ -28,7 +29,6 @@ export default function PromotionIndexComponent ({}) {
return ( return (
<div> <div>
<div className="promo-container"> <div className="promo-container">
<div className="promo-list-container"> <div className="promo-list-container">
<div className="promo-header"> <div className="promo-header">
<h1 className="promo-title"> Promotions</h1> <h1 className="promo-title"> Promotions</h1>
...@@ -58,7 +58,7 @@ export default function PromotionIndexComponent ({}) { ...@@ -58,7 +58,7 @@ export default function PromotionIndexComponent ({}) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{promoData.map((promo, key) => { {promoData.length > 0 && promoData.map((promo, key) => {
return ( return (
<tr key={key}> <tr key={key}>
<td> <td>
...@@ -76,7 +76,7 @@ export default function PromotionIndexComponent ({}) { ...@@ -76,7 +76,7 @@ export default function PromotionIndexComponent ({}) {
<td> <td>
<NavLink className="edit-navLink" to={{ <NavLink className="edit-navLink" to={{
pathname: `/promos/${promo.promotionId}/update`, pathname: `/promos/${promo.promotionId}/update`,
state: {promo} state: {promo}
}}> }}>
<button className="btn-primary">Update</button> <button className="btn-primary">Update</button>
</NavLink> </NavLink>
...@@ -86,12 +86,12 @@ export default function PromotionIndexComponent ({}) { ...@@ -86,12 +86,12 @@ export default function PromotionIndexComponent ({}) {
</td> </td>
</tr> </tr>
) )
}).reverse() }).reverse() }
}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
) )
} }
\ No newline at end of file
.promo-container { .promo-container {
margin: 30px auto; margin: 30px auto;
padding: 0 16px; padding: 0 16px;
} }
.promo-header { .promo-header {
background-color: #212529; background-color: #212529;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
} }
.promo-title { .promo-title {
color: white;
color: white; padding: 8px;
padding: 8px; }
}
.add-navLink { .add-navLink {
align-self: center; align-self: center;
} }
table { table {
...@@ -25,17 +24,18 @@ table { ...@@ -25,17 +24,18 @@ table {
width: 100%; width: 100%;
} }
td, th { td,
th {
text-align: left; text-align: left;
padding: 8px; padding: 8px;
} }
tr td { tr td {
height: 40px; height: 40px;
} }
tr { tr {
box-sizing: border-box; box-sizing: border-box;
} }
tr:nth-child(even) { tr:nth-child(even) {
...@@ -43,10 +43,10 @@ tr:nth-child(even) { ...@@ -43,10 +43,10 @@ tr:nth-child(even) {
} }
thead th { thead th {
background-color: #dddddd; background-color: #dddddd;
height: 35px; height: 35px;
} }
tr:hover { tr:hover {
background-color: lightblue; background-color: lightblue;
} }
\ No newline at end of file
...@@ -30,15 +30,15 @@ export default function PromotionNewFormComponent (props) { ...@@ -30,15 +30,15 @@ export default function PromotionNewFormComponent (props) {
}; };
useEffect(async () => { useEffect(() => {
await loadProducts(); loadProducts();
}, []) }, [])
const loadProducts = async (event) => { const loadProducts = async (event) => {
const data = await getAllProducts(); const data = await getAllProducts();
setproductData(data); setproductData(data);
} }
const onChange = (e, data) => { const onChange = (e, data) => {
...@@ -49,18 +49,19 @@ export default function PromotionNewFormComponent (props) { ...@@ -49,18 +49,19 @@ export default function PromotionNewFormComponent (props) {
const productSku = register('productSku', { required: true }) const productSku = register('productSku', { required: true })
return ( return (
<div> <div>
<div className="promo-container"> <div className="promo-container">
<div className="promo-form-container"> <div className="promo-form-container">
<h1 className="promo-form-title">Add Promotion</h1> <h1 className="promo-form-title">Add Promotion</h1>
<form className="promo-form" onSubmit={handleSubmit(onSubmit)}> <form className="promo-form" onSubmit={handleSubmit(onSubmit)}>
{serverErrors.length ? <p className="form-error">{serverErrors}</p> : ""} {serverErrors.length ? <p className="form-error">{serverErrors}</p> : ""}
<div className="form-group"> <div className="form-group">
<label>Promotion Id</label> <label>Promotion Id</label>
<input <input
{...register("promotionId", { {...register("promotionId", {
required: "promotion id is required", required: "promotion id is required",
maxLength: { value: 10, message: "You exceeded the maximum value" } maxLength: { value: 10, message: "You exceeded the maximum value" }
...@@ -69,10 +70,11 @@ export default function PromotionNewFormComponent (props) { ...@@ -69,10 +70,11 @@ export default function PromotionNewFormComponent (props) {
className="form-control" className="form-control"
placeholder="eg. PROMO-403" placeholder="eg. PROMO-403"
/> />
{errors.promotionId && <p className="form-error">{errors.promotionId.message}</p>}
{errors.promotionId && <p className="form-error">{errors.promotionId.message}</p>}
</div> </div>
<div className="form-group"> <div className="form-group">
<label>Product Sku</label> <label>Product Sku</label>
{/* <input {/* <input
...@@ -108,7 +110,7 @@ export default function PromotionNewFormComponent (props) { ...@@ -108,7 +110,7 @@ export default function PromotionNewFormComponent (props) {
<div className="form-group"> <div className="form-group">
<label>Enter % off</label> <label>Enter % off</label>
<div className="input-group"> <div className="input-group">
<div className="input-group-prepend"> <div className="input-group-prepend">
<span className="input-group-text">%</span></div> <span className="input-group-text">%</span></div>
<input <input
...@@ -130,7 +132,7 @@ export default function PromotionNewFormComponent (props) { ...@@ -130,7 +132,7 @@ export default function PromotionNewFormComponent (props) {
<div className="form-group"> <div className="form-group">
<label>Applies at Minimum Quantity</label> <label>Applies at Minimum Quantity</label>
<input <input
{...register("minimumQuantity", { {...register("minimumQuantity", {
valueAsNumber: true, valueAsNumber: true,
required: "minimum quantity is required", required: "minimum quantity is required",
...@@ -153,4 +155,3 @@ export default function PromotionNewFormComponent (props) { ...@@ -153,4 +155,3 @@ export default function PromotionNewFormComponent (props) {
</div> </div>
); );
} }
...@@ -29,8 +29,8 @@ export default function PromotionUpdateFormComponent(props) { ...@@ -29,8 +29,8 @@ export default function PromotionUpdateFormComponent(props) {
.catch(err => setErrors([err.message])) .catch(err => setErrors([err.message]))
}; };
useEffect(async () => { useEffect( () => {
await loadProducts(); loadProducts();
}, []) }, [])
const loadProducts = async (event) => { const loadProducts = async (event) => {
...@@ -59,13 +59,13 @@ export default function PromotionUpdateFormComponent(props) { ...@@ -59,13 +59,13 @@ export default function PromotionUpdateFormComponent(props) {
value: props.location.state.promo.promotionId, value: props.location.state.promo.promotionId,
required: "promotion id is required", required: "promotion id is required",
maxLength: { value: 20, message: "You exceeded the maximum value" }, maxLength: { value: 20, message: "You exceeded the maximum value" },
})} })}
readOnly readOnly
id="promotionId" id="promotionId"
className="form-control" className="form-control"
placeholder="eg. PROMO-403" placeholder="eg. PROMO-403"
/> />
{errors.promotionId && <p className="form-error">{errors.promotionId.message}</p>} {errors.promotionId && <p className="form-error">{errors.promotionId.message}</p>}
...@@ -142,4 +142,3 @@ export default function PromotionUpdateFormComponent(props) { ...@@ -142,4 +142,3 @@ export default function PromotionUpdateFormComponent(props) {
</div> </div>
); );
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
} }
.promo-form-title { .promo-form-title {
text-align: center; text-align: center;
background-color: #212529; background-color: #212529;
...@@ -17,11 +18,6 @@ ...@@ -17,11 +18,6 @@
border: 1px solid #212529; border: 1px solid #212529;
} }
label {
font-weight: bold;
padding-bottom: 7px;
}
.promo-form { .promo-form {
padding: 50px; padding: 50px;
......
...@@ -3,4 +3,4 @@ class Config { ...@@ -3,4 +3,4 @@ class Config {
static promotionsUrl = "http://localhost:8081/api/promos"; static promotionsUrl = "http://localhost:8081/api/promos";
} }
export default Config; export default Config;
\ No newline at end of file
const emptyProduct = {
sku: "",
upc: "",
productName: "",
brand: "",
category: "",
price: 0.0,
availableStock: 0,
productDesciption: "",
productImageUrl: "",
};
export default emptyProduct;
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
margin-left: 1em; margin-left: 1em;
} }
#prod-grid { #prod-table {
margin-left: 2em; margin-left: 5em;
} }
\ No newline at end of file
img {
max-width: 100%;
max-height: 250px;
}
.img-container{ .img-container{
position:relative; position:relative;
overflow:hidden; overflow:hidden;
padding-bottom:100%; padding-bottom:100%;
} }
img { .grid-img {
position: absolute; position: absolute;
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translateX(-50%) translateY(-50%); transform: translateX(-50%) translateY(-50%);
}
.modal-img {
display: flex;
justify-content: center;
}
.alert{
width: 100%;
} }
\ No newline at end of file
.form-check-label{
color: rgba(255, 255, 255, 0.55)
}
.form-check.form-check-inline{
margin-top: 0.35rem;
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
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