-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from dladlk/feature/1-ordering
add to basker, basket page, order header
- Loading branch information
Showing
22 changed files
with
811 additions
and
313 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,4 +50,6 @@ dist/ | |
log/ | ||
|
||
# Skip VisualStudio Code folders | ||
.vscode/ | ||
.vscode/ | ||
|
||
.tomcat/ |
172 changes: 85 additions & 87 deletions
172
cm-api/src/main/java/dk/erst/cm/api/item/ProductService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,101 +1,99 @@ | ||
package dk.erst.cm.api.item; | ||
|
||
import java.time.Instant; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import dk.erst.cm.api.dao.mongo.ProductRepository; | ||
import dk.erst.cm.api.data.Product; | ||
import dk.erst.cm.api.data.ProductCatalogUpdate; | ||
import dk.erst.cm.xml.ubl21.model.CatalogueLine; | ||
import dk.erst.cm.xml.ubl21.model.NestedSchemeID; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.mongodb.core.query.TextCriteria; | ||
import org.springframework.stereotype.Service; | ||
|
||
import dk.erst.cm.api.dao.mongo.ProductRepository; | ||
import dk.erst.cm.api.data.Product; | ||
import dk.erst.cm.api.data.ProductCatalogUpdate; | ||
import dk.erst.cm.xml.ubl21.model.CatalogueLine; | ||
import dk.erst.cm.xml.ubl21.model.NestedSchemeID; | ||
import java.time.Instant; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
@Service | ||
public class ProductService { | ||
|
||
private ProductRepository productRepository; | ||
|
||
@Autowired | ||
public ProductService(ProductRepository itemRepository) { | ||
this.productRepository = itemRepository; | ||
} | ||
|
||
public Product saveCatalogUpdateItem(ProductCatalogUpdate catalog, CatalogueLine line) { | ||
String lineLogicalId = line.getLogicalId(); | ||
String productCatalogId = catalog.getProductCatalogId(); | ||
|
||
String itemLogicalId = productCatalogId + "_" + lineLogicalId; | ||
|
||
boolean deleteAction = line.getActionCode() != null && "Delete".equals(line.getActionCode().getId()); | ||
|
||
Product product; | ||
Optional<Product> optional = productRepository.findById(itemLogicalId); | ||
if (optional.isPresent()) { | ||
product = optional.get(); | ||
product.setUpdateTime(Instant.now()); | ||
product.setVersion(product.getVersion() + 1); | ||
} else { | ||
product = new Product(); | ||
product.setId(itemLogicalId); | ||
product.setCreateTime(Instant.now()); | ||
product.setUpdateTime(null); | ||
product.setVersion(1); | ||
} | ||
product.setDocumentVersion(ProductDocumentVersion.PEPPOL_CATALOGUE_3_1); | ||
product.setProductCatalogId(productCatalogId); | ||
product.setStandardNumber(getLineStandardNumber(line)); | ||
product.setDocument(line); | ||
|
||
if (deleteAction) { | ||
if (optional.isPresent()) { | ||
productRepository.delete(product); | ||
} | ||
return null; | ||
} | ||
productRepository.save(product); | ||
|
||
return product; | ||
} | ||
|
||
private String getLineStandardNumber(CatalogueLine line) { | ||
if (line != null && line.getItem() != null) { | ||
if (line.getItem().getStandardItemIdentification() != null) { | ||
NestedSchemeID sn = line.getItem().getStandardItemIdentification(); | ||
if (sn.getId() != null && sn.getId().getId() != null) { | ||
return sn.getId().getId().toUpperCase(); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public long countItems() { | ||
return productRepository.count(); | ||
} | ||
|
||
public Page<Product> findAll(String searchParam, Pageable pageable) { | ||
Page<Product> productList; | ||
if (!StringUtils.isEmpty(searchParam)) { | ||
TextCriteria textCriteria = TextCriteria.forDefaultLanguage().matching(searchParam); | ||
productList = productRepository.findAllBy(textCriteria, pageable); | ||
} else { | ||
productList = productRepository.findAll(pageable); | ||
} | ||
return productList; | ||
} | ||
|
||
public Optional<Product> findById(String id) { | ||
return productRepository.findById(id); | ||
} | ||
|
||
public List<Product> findByStandardNumber(String standardNumber) { | ||
return productRepository.findByStandardNumber(standardNumber); | ||
} | ||
private final ProductRepository productRepository; | ||
|
||
@Autowired | ||
public ProductService(ProductRepository itemRepository) { | ||
this.productRepository = itemRepository; | ||
} | ||
|
||
public Product saveCatalogUpdateItem(ProductCatalogUpdate catalog, CatalogueLine line) { | ||
String lineLogicalId = line.getLogicalId(); | ||
String productCatalogId = catalog.getProductCatalogId(); | ||
String itemLogicalId = productCatalogId + "_" + lineLogicalId; | ||
boolean deleteAction = line.getActionCode() != null && "Delete".equals(line.getActionCode().getId()); | ||
Product product; | ||
Optional<Product> optional = productRepository.findById(itemLogicalId); | ||
if (optional.isPresent()) { | ||
product = optional.get(); | ||
product.setUpdateTime(Instant.now()); | ||
product.setVersion(product.getVersion() + 1); | ||
} else { | ||
product = new Product(); | ||
product.setId(itemLogicalId); | ||
product.setCreateTime(Instant.now()); | ||
product.setUpdateTime(null); | ||
product.setVersion(1); | ||
} | ||
product.setDocumentVersion(ProductDocumentVersion.PEPPOL_CATALOGUE_3_1); | ||
product.setProductCatalogId(productCatalogId); | ||
product.setStandardNumber(getLineStandardNumber(line)); | ||
product.setDocument(line); | ||
if (deleteAction) { | ||
if (optional.isPresent()) { | ||
productRepository.delete(product); | ||
} | ||
return null; | ||
} | ||
productRepository.save(product); | ||
return product; | ||
} | ||
|
||
private String getLineStandardNumber(CatalogueLine line) { | ||
if (line != null && line.getItem() != null) { | ||
if (line.getItem().getStandardItemIdentification() != null) { | ||
NestedSchemeID sn = line.getItem().getStandardItemIdentification(); | ||
if (sn.getId() != null && sn.getId().getId() != null) { | ||
return sn.getId().getId().toUpperCase(); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
public long countItems() { | ||
return productRepository.count(); | ||
} | ||
|
||
public Page<Product> findAll(String searchParam, Pageable pageable) { | ||
Page<Product> productList; | ||
if (!StringUtils.isEmpty(searchParam)) { | ||
TextCriteria textCriteria = TextCriteria.forDefaultLanguage().matching(searchParam); | ||
productList = productRepository.findAllBy(textCriteria, pageable); | ||
} else { | ||
productList = productRepository.findAll(pageable); | ||
} | ||
return productList; | ||
} | ||
|
||
public Optional<Product> findById(String id) { | ||
return productRepository.findById(id); | ||
} | ||
|
||
public Iterable<Product> findAllByIds(Iterable<String> ids) { | ||
return productRepository.findAllById(ids); | ||
} | ||
|
||
public List<Product> findByStandardNumber(String standardNumber) { | ||
return productRepository.findByStandardNumber(standardNumber); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import {Button} from "@material-ui/core"; | ||
import React, {useEffect, useRef} from "react"; | ||
import CircularProgress from "@material-ui/core/CircularProgress"; | ||
import {ProductBasketStatus} from "./BasketData"; | ||
|
||
|
||
export default function AddToBasket(props) { | ||
|
||
const {changeBasket, basketData, product} = props; | ||
|
||
const [state, setState] = React.useState(basketData.getProductBasketStatus(product.id)); | ||
|
||
const getButtonTitle = () => { | ||
switch (state) { | ||
case ProductBasketStatus.Adding: | ||
return 'Adding to basket'; | ||
case ProductBasketStatus.Added: | ||
return 'Remove from basket'; | ||
default: | ||
return 'Add to basket'; | ||
} | ||
} | ||
|
||
const isProgress = () => { | ||
return state === ProductBasketStatus.Adding; | ||
} | ||
|
||
// TODO: Remove - temporary code to imitate slow adding | ||
const timerRef = useRef(null); | ||
const handleClick = () => { | ||
if (state === ProductBasketStatus.Empty) { | ||
setState(ProductBasketStatus.Adding); | ||
timerRef.current = setTimeout(() => { | ||
changeBasket(product.id, 1); | ||
setState(ProductBasketStatus.Added) | ||
}, 300); | ||
} else if (state === ProductBasketStatus.Added) { | ||
changeBasket(product.id, 0); | ||
setState(ProductBasketStatus.Empty); | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
return () => clearTimeout(timerRef.current) | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<Button variant="outlined" size={"small"} color="primary" onClick={() => handleClick()}> | ||
{isProgress() && ( | ||
<CircularProgress size={'1rem'} style={{marginRight: '5px'}}/> | ||
)} | ||
{getButtonTitle()} | ||
</Button> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react'; | ||
import {makeStyles} from '@material-ui/core/styles'; | ||
import ShoppingBasketIcon from '@material-ui/icons/ShoppingBasket'; | ||
|
||
const useStyles = makeStyles((theme) => ({ | ||
basket: { | ||
padding: theme.spacing(0, 2), | ||
}, | ||
basketIcon: { | ||
color: theme.palette.common.white | ||
}, | ||
})); | ||
|
||
export default function BasketBar() { | ||
const classes = useStyles(); | ||
|
||
return ( | ||
|
||
<div className={classes.basket}> | ||
<div className={classes.basketIcon}> | ||
<ShoppingBasketIcon/> | ||
</div> | ||
</div> | ||
|
||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
export const ProductBasketStatus = { | ||
Empty: 'empty', | ||
Adding: 'adding', | ||
Added: 'added', | ||
} | ||
|
||
export function createBasketData() { | ||
|
||
class BasketData { | ||
constructor(orderLines = {}, orderLinesCount = 0) { | ||
this.orderLines = orderLines; | ||
this.orderLinesCount = orderLinesCount; | ||
} | ||
|
||
isEmpty() { | ||
return this.orderLinesCount === 0; | ||
} | ||
|
||
getOrderLineList() { | ||
return Object.keys(this.orderLines).map((productId) => { | ||
return {productId: productId, quantity: this.orderLines[productId]} | ||
}); | ||
} | ||
|
||
changeBasket(productId, quantity) { | ||
let newOrderLines = {...this.orderLines}; | ||
let newOrderLinesCount = this.orderLinesCount; | ||
if (productId in newOrderLines) { | ||
if (quantity !== 0) { | ||
newOrderLines[productId] += quantity; | ||
} else { | ||
delete newOrderLines[productId] | ||
newOrderLinesCount--; | ||
} | ||
} else { | ||
newOrderLines[productId] = quantity; | ||
newOrderLinesCount++; | ||
} | ||
return new BasketData(newOrderLines, newOrderLinesCount); | ||
} | ||
|
||
getProductBasketStatus(productId) { | ||
return productId in this.orderLines ? ProductBasketStatus.Added : ProductBasketStatus.Empty; | ||
} | ||
} | ||
|
||
return new BasketData(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import {Grid, Paper, TextField, Typography} from "@material-ui/core"; | ||
import {makeStyles} from "@material-ui/core/styles"; | ||
|
||
export default function OrderHeader() { | ||
|
||
const useStyles = makeStyles((theme) => ({ | ||
paper: { | ||
padding: theme.spacing(2), | ||
marginBottom: theme.spacing(2), | ||
}, | ||
formHeader: { | ||
paddingTop: theme.spacing(1), | ||
paddingLeft: theme.spacing(2), | ||
textAlign: "left", | ||
fontSize: '1em', | ||
}, | ||
form: { | ||
padding: theme.spacing(2), | ||
display: "flex", | ||
flex: "1", | ||
flexDirection: "row", | ||
justifyContent: "space-between", | ||
}, | ||
input: { | ||
paddingInline: theme.spacing(0.5), | ||
} | ||
})); | ||
|
||
const classes = useStyles(); | ||
|
||
function DataInput(props) { | ||
return <TextField className={classes.input} size={"small"} {...props} /> | ||
} | ||
|
||
function DataBlock(props) { | ||
return <Grid item sm={12} md={6}> | ||
<Paper> | ||
<div className={classes.formHeader}><Typography variant="h6">{props.name}</Typography></div> | ||
<form className={classes.form} noValidate autoComplete="on"> | ||
{props.children} | ||
</form> | ||
</Paper> | ||
</Grid> | ||
|
||
} | ||
|
||
return <Paper className={classes.paper}> | ||
<Grid container spacing={2}> | ||
<DataBlock name={"Buyer company"}> | ||
<DataInput label="Registration name" defaultValue="My Company ApS" required/> | ||
<DataInput label="Legal identifier" defaultValue="DK11223344" required/> | ||
<DataInput label="Party identifier" defaultValue="7300010000001" required/> | ||
</DataBlock> | ||
<DataBlock name={"Buyer contact"}> | ||
<DataInput label="Person name" defaultValue="John Dohn"/> | ||
<DataInput label="Email" defaultValue="[email protected]"/> | ||
<DataInput label="Telephone" defaultValue="+45 11223344"/> | ||
</DataBlock> | ||
</Grid> | ||
</Paper> | ||
} |
Oops, something went wrong.