Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Samuel Dudík
PA165 Winery Management System
Commits
ef752cef
Commit
ef752cef
authored
May 18, 2022
by
Samuel Dudík
Browse files
Merge branch 'feature/dudik/mvc-user-orders' into 'main'
Shopping cart and orders See merge request
!44
parents
6b6a4b2b
8e539b3d
Pipeline
#141069
passed with stages
in 2 minutes and 19 seconds
Changes
12
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
winery/api/src/main/java/cz/muni/fi/pa165/winery/services/CartService.java
View file @
ef752cef
...
@@ -6,5 +6,6 @@ import cz.muni.fi.pa165.winery.dto.order.OrderDto;
...
@@ -6,5 +6,6 @@ import cz.muni.fi.pa165.winery.dto.order.OrderDto;
public
interface
CartService
{
public
interface
CartService
{
CartDto
loadFromCookie
();
CartDto
loadFromCookie
();
void
saveToCookie
(
CartDto
cart
);
void
saveToCookie
(
CartDto
cart
);
void
clear
();
OrderDto
convertToOrder
(
CartDto
cart
,
long
userId
);
OrderDto
convertToOrder
(
CartDto
cart
,
long
userId
);
}
}
winery/dao/src/main/java/cz/muni/fi/pa165/winery/persistence/dao/DaoBaseImpl.java
View file @
ef752cef
...
@@ -51,7 +51,7 @@ public class DaoBaseImpl<ENTITY extends EntityBase> implements DaoBase<ENTITY> {
...
@@ -51,7 +51,7 @@ public class DaoBaseImpl<ENTITY extends EntityBase> implements DaoBase<ENTITY> {
public
ENTITY
get
(
long
id
,
boolean
useCachedResults
)
{
public
ENTITY
get
(
long
id
,
boolean
useCachedResults
)
{
try
{
try
{
var
entity
=
entityManager
.
find
(
entityClass
,
id
);
var
entity
=
entityManager
.
find
(
entityClass
,
id
);
if
(!
useCachedResults
)
{
if
(!
useCachedResults
&&
entity
!=
null
)
{
entityManager
.
refresh
(
entity
);
entityManager
.
refresh
(
entity
);
}
}
return
entity
;
return
entity
;
...
...
winery/service/src/main/java/cz/muni/fi/pa165/winery/services/CartServiceImpl.java
View file @
ef752cef
...
@@ -53,6 +53,11 @@ public class CartServiceImpl implements CartService {
...
@@ -53,6 +53,11 @@ public class CartServiceImpl implements CartService {
cookieService
.
setCookie
(
getCartCookieName
(),
json
);
cookieService
.
setCookie
(
getCartCookieName
(),
json
);
}
}
@Override
public
void
clear
()
{
cookieService
.
setCookie
(
getCartCookieName
(),
"{}"
);
}
@Override
@Override
public
OrderDto
convertToOrder
(
CartDto
cart
,
long
userId
)
{
public
OrderDto
convertToOrder
(
CartDto
cart
,
long
userId
)
{
var
order
=
new
OrderDto
();
var
order
=
new
OrderDto
();
...
...
winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/CartController.java
View file @
ef752cef
...
@@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.GetMapping;
...
@@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
org.springframework.web.server.ResponseStatusException
;
import
org.springframework.web.servlet.ModelAndView
;
import
org.springframework.web.servlet.ModelAndView
;
import
java.math.BigDecimal
;
import
java.math.BigDecimal
;
...
@@ -24,6 +25,8 @@ import java.util.HashSet;
...
@@ -24,6 +25,8 @@ import java.util.HashSet;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
import
java.util.stream.Collectors
;
import
static
org
.
springframework
.
http
.
HttpStatus
.
GONE
;
@Controller
@Controller
@RequestMapping
(
"/cart"
)
@RequestMapping
(
"/cart"
)
public
class
CartController
extends
ControllerBase
{
public
class
CartController
extends
ControllerBase
{
...
@@ -41,7 +44,7 @@ public class CartController extends ControllerBase {
...
@@ -41,7 +44,7 @@ public class CartController extends ControllerBase {
}
}
@GetMapping
(
""
)
@GetMapping
(
""
)
public
ModelAndView
test
()
{
public
ModelAndView
index
()
{
var
viewModel
=
new
CartListingViewModel
();
var
viewModel
=
new
CartListingViewModel
();
initializeViewModel
(
viewModel
);
initializeViewModel
(
viewModel
);
...
@@ -63,6 +66,9 @@ public class CartController extends ControllerBase {
...
@@ -63,6 +66,9 @@ public class CartController extends ControllerBase {
@PostMapping
(
"/add"
)
@PostMapping
(
"/add"
)
@ResponseBody
@ResponseBody
public
String
add
(
long
id
,
int
count
)
{
public
String
add
(
long
id
,
int
count
)
{
if
(
count
<
1
)
return
"Fail"
;
var
cart
=
cartService
.
loadFromCookie
();
var
cart
=
cartService
.
loadFromCookie
();
cart
.
add
(
id
,
count
);
cart
.
add
(
id
,
count
);
cartService
.
saveToCookie
(
cart
);
cartService
.
saveToCookie
(
cart
);
...
@@ -90,6 +96,19 @@ public class CartController extends ControllerBase {
...
@@ -90,6 +96,19 @@ public class CartController extends ControllerBase {
@PostMapping
(
"/checkout"
)
@PostMapping
(
"/checkout"
)
@ResponseBody
@ResponseBody
public
String
add
()
{
public
String
add
()
{
var
cart
=
cartService
.
loadFromCookie
();
var
cartItems
=
cart
.
getCartItems
();
for
(
Map
.
Entry
<
Long
,
Integer
>
entry
:
cartItems
.
entrySet
())
{
var
bottle
=
wineBottleService
.
get
(
entry
.
getKey
());
if
(
bottle
==
null
)
{
continue
;
}
if
(
bottle
.
getStock
()
<
entry
.
getValue
())
throw
new
ResponseStatusException
(
GONE
,
"Not enough products on stock."
);
}
var
order
=
new
OrderDto
();
var
order
=
new
OrderDto
();
order
.
setShipped
(
false
);
order
.
setShipped
(
false
);
...
@@ -98,27 +117,35 @@ public class CartController extends ControllerBase {
...
@@ -98,27 +117,35 @@ public class CartController extends ControllerBase {
orderService
.
create
(
order
);
orderService
.
create
(
order
);
var
cart
=
cartService
.
loadFromCookie
();
var
cartItems
=
cart
.
getCartItems
();
for
(
Map
.
Entry
<
Long
,
Integer
>
entry
:
cartItems
.
entrySet
())
{
for
(
Map
.
Entry
<
Long
,
Integer
>
entry
:
cartItems
.
entrySet
())
{
var
item
=
new
OrderItemDto
();
var
item
=
new
OrderItemDto
();
item
.
setBottleId
(
entry
.
getKey
());
item
.
setBottleId
(
entry
.
getKey
());
item
.
setQuantity
(
new
BigDecimal
(
entry
.
getValue
()));
item
.
setQuantity
(
new
BigDecimal
(
entry
.
getValue
()));
item
.
setOrderId
(
order
.
getId
());
item
.
setOrderId
(
order
.
getId
());
item
.
setOrderId
(
order
.
getId
());
order
.
getItems
().
add
(
item
);
order
.
getItems
().
add
(
item
);
orderItemService
.
create
(
item
);
cart
.
remove
(
entry
.
getKey
());
var
bottle
=
wineBottleService
.
get
(
item
.
getBottleId
());
var
bottle
=
wineBottleService
.
get
(
item
.
getBottleId
());
if
(
bottle
==
null
)
{
continue
;
}
bottle
.
setStock
(
bottle
.
getStock
()
-
item
.
getQuantity
().
intValue
());
bottle
.
setStock
(
bottle
.
getStock
()
-
item
.
getQuantity
().
intValue
());
orderItemService
.
create
(
item
);
wineBottleService
.
update
(
bottle
);
wineBottleService
.
update
(
bottle
);
}
}
cartService
.
saveToCookie
(
c
ar
t
);
cartService
.
cle
ar
(
);
return
"Success"
;
return
"Success"
;
}
}
@Override
protected
<
T
extends
ViewModelBase
>
void
initializeViewModel
(
T
viewModel
)
{
super
.
initializeViewModel
(
viewModel
);
if
(
viewModel
instanceof
CartListingViewModel
)
{
var
cartViewModel
=
(
CartListingViewModel
)
viewModel
;
var
productNames
=
wineBottleService
.
getAll
().
stream
().
collect
(
Collectors
.
toMap
(
x
->
x
.
getId
(),
x
->
x
.
getName
()));
cartViewModel
.
setProductNames
(
productNames
);
}
}
}
}
winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/ControllerBase.java
View file @
ef752cef
...
@@ -112,7 +112,9 @@ public class ControllerBase {
...
@@ -112,7 +112,9 @@ public class ControllerBase {
private
Pair
<
String
,
String
>
getControllerAndAction
()
{
private
Pair
<
String
,
String
>
getControllerAndAction
()
{
var
callerFrame
=
StackWalker
.
getInstance
(
StackWalker
.
Option
.
RETAIN_CLASS_REFERENCE
)
var
callerFrame
=
StackWalker
.
getInstance
(
StackWalker
.
Option
.
RETAIN_CLASS_REFERENCE
)
.
walk
(
s
->
.
walk
(
s
->
s
.
filter
(
c
->
ControllerBase
.
class
.
isAssignableFrom
(
c
.
getDeclaringClass
())
&&
c
.
getDeclaringClass
()
!=
ControllerBase
.
class
)
s
.
filter
(
c
->
ControllerBase
.
class
.
isAssignableFrom
(
c
.
getDeclaringClass
())
&&
c
.
getDeclaringClass
()
!=
ControllerBase
.
class
&&
!
c
.
getMethodName
().
equals
(
"initializeViewModel"
))
.
findFirst
());
.
findFirst
());
if
(!
callerFrame
.
isPresent
())
{
if
(!
callerFrame
.
isPresent
())
{
...
...
winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/controllers/OrderController.java
View file @
ef752cef
...
@@ -33,9 +33,6 @@ public class OrderController extends ControllerBase {
...
@@ -33,9 +33,6 @@ public class OrderController extends ControllerBase {
@GetMapping
(
""
)
@GetMapping
(
""
)
@Secured
(
"ROLE_ADMIN"
)
@Secured
(
"ROLE_ADMIN"
)
public
ModelAndView
index
()
{
public
ModelAndView
index
()
{
// var orders = orderService.getAll();
// TODO not pretty
var
orders
=
new
ArrayList
<
OrderDto
>();
var
orders
=
new
ArrayList
<
OrderDto
>();
for
(
OrderDto
order
:
orderService
.
getAll
())
{
for
(
OrderDto
order
:
orderService
.
getAll
())
{
...
...
winery/webapp/src/main/java/cz/muni/fi/pa165/winery/webapp/models/cart/CartListingViewModel.java
View file @
ef752cef
...
@@ -5,9 +5,11 @@ import lombok.Getter;
...
@@ -5,9 +5,11 @@ import lombok.Getter;
import
lombok.Setter
;
import
lombok.Setter
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
@Getter
@Getter
@Setter
@Setter
public
class
CartListingViewModel
extends
ViewModelBase
{
public
class
CartListingViewModel
extends
ViewModelBase
{
private
List
<
CartItemViewModel
>
cart
;
private
List
<
CartItemViewModel
>
cart
;
private
Map
<
Long
,
String
>
productNames
;
}
}
winery/webapp/src/main/resources/static/js/scripts.js
View file @
ef752cef
...
@@ -24,8 +24,14 @@ function setCartAmount(itemId, count) {
...
@@ -24,8 +24,14 @@ function setCartAmount(itemId, count) {
function
checkout
()
{
function
checkout
()
{
return
fetch
(
`
${
getUrlPrefix
()}
cart/checkout`
,
return
fetch
(
`
${
getUrlPrefix
()}
cart/checkout`
,
{
method
:
'
POST
'
}).
then
(()
=>
{
{
method
:
'
POST
'
}).
then
((
res
)
=>
{
window
.
location
.
href
=
`
${
getUrlPrefix
()}
myorder`
;
if
(
res
.
status
==
410
)
{
document
.
getElementById
(
"
failure-alert
"
).
style
.
display
=
"
block
"
;
document
.
getElementById
(
"
success-alert
"
).
style
.
display
=
"
none
"
;
}
else
{
document
.
getElementById
(
"
success-alert
"
).
style
.
display
=
"
block
"
;
document
.
getElementById
(
"
failure-alert
"
).
style
.
display
=
"
none
"
;
}
});
});
}
}
...
...
winery/webapp/src/main/resources/templates/cart/
test
.html
→
winery/webapp/src/main/resources/templates/cart/
index
.html
View file @
ef752cef
...
@@ -10,22 +10,34 @@
...
@@ -10,22 +10,34 @@
<div
class=
"container px-4 px-lg-5"
>
<div
class=
"container px-4 px-lg-5"
>
<main
role=
"main"
class=
"pb-3"
>
<main
role=
"main"
class=
"pb-3"
>
<h4>
Cart contents:
</h4>
<h4>
Cart contents:
</h4>
<div
style=
"display: none;"
id=
"success-alert"
>
<div
class=
"alert alert-success mt-2"
role=
"alert"
>
The order was sent successfully!
</div>
</div>
<div
style=
"display: none;"
id=
"failure-alert"
>
<div
class=
"alert alert-danger mt-2"
role=
"alert"
>
Not enough products on stock!
</div>
</div>
<table
class=
"table"
>
<table
class=
"table"
>
<thead>
<thead>
<th>
Item ID
</th>
<th>
Product Name
</th>
<th>
Count
</th>
<th>
Count
</th>
<th>
Delete
</th>
</thead>
</thead>
<tbody>
<tbody>
<tr
th:each=
"item : ${model.cart}"
>
<tr
th:each=
"item : ${model.cart}"
>
<td
th:text=
"${item.productId}"
>
ID
</td>
<td
th:text=
"${model.productNames[__${item.productId}__]}"
>
Product Name
</td>
<td
th:text=
"${item.count}"
>
Count
</td>
<td><input
th:onchange=
"'setCartAmount(' + ${item.productId} + ', document.getElementById(\'quantity\').value)'"
type=
"number"
id=
"quantity"
name=
"quantity"
min=
"1"
th:value=
"${item.count}"
/></td>
<td><a
href=
"#"
th:onclick=
"'removeFromCart(' + ${item.productId} + ').then(x => refreshPage())'"
>
Delete
</a></td>
</tr>
</tr>
</tbody>
</tbody>
</table>
</table>
<a
class=
"btn btn-primary"
href=
"#"
onclick=
"addToCart(1, 3).then(x => refreshPage())"
>
Add to cart
</a>
<a
class=
"btn btn-primary"
href=
"#"
onclick=
"removeFromCart(1).then(x => refreshPage())"
>
Remove from cart
</a>
<a
class=
"btn btn-primary"
href=
"#"
onclick=
"setCartAmount(1, 2).then(x => refreshPage())"
>
Set cart amount
</a>
<a
class=
"btn btn-primary"
href=
"#"
onclick=
"checkout()"
>
Checkout
</a>
<a
class=
"btn btn-primary"
href=
"#"
onclick=
"checkout()"
>
Checkout
</a>
</main>
</main>
</div>
</div>
...
...
winery/webapp/src/main/resources/templates/fragments/header.html
View file @
ef752cef
...
@@ -31,7 +31,6 @@
...
@@ -31,7 +31,6 @@
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/productreview}"
>
Reviews
</a></li>
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/productreview}"
>
Reviews
</a></li>
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/user}"
th:if=
'${model.currentUser != null && model.currentUser.hasRole("ADMIN")}'
>
Users
</a></li>
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/user}"
th:if=
'${model.currentUser != null && model.currentUser.hasRole("ADMIN")}'
>
Users
</a></li>
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/order}"
th:if=
'${model.currentUser != null && model.currentUser.hasRole("ADMIN")}'
>
Orders
</a></li>
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/order}"
th:if=
'${model.currentUser != null && model.currentUser.hasRole("ADMIN")}'
>
Orders
</a></li>
<li
class=
"nav-item"
><a
class=
"nav-link active"
th:href=
"@{/cart}"
th:if=
'${model.currentUser != null && model.currentUser.hasRole("ADMIN")}'
>
Cart test
</a></li>
<div
class=
"vr"
th:if=
"${model.currentUser}"
></div>
<div
class=
"vr"
th:if=
"${model.currentUser}"
></div>
<li
class=
"nav-item dropdown"
th:if=
"${model.currentUser}"
>
<li
class=
"nav-item dropdown"
th:if=
"${model.currentUser}"
>
<a
class=
"nav-link dropdown-toggle"
href=
"#"
id=
"navbarDropdown"
role=
"button"
data-bs-toggle=
"dropdown"
aria-haspopup=
"true"
aria-expanded=
"false"
th:text=
"${model.currentUser.email}"
>
<a
class=
"nav-link dropdown-toggle"
href=
"#"
id=
"navbarDropdown"
role=
"button"
data-bs-toggle=
"dropdown"
aria-haspopup=
"true"
aria-expanded=
"false"
th:text=
"${model.currentUser.email}"
>
...
...
winery/webapp/src/main/resources/templates/order/edit.html
View file @
ef752cef
...
@@ -43,7 +43,7 @@
...
@@ -43,7 +43,7 @@
<h3>
Items
</h3>
<h3>
Items
</h3>
<div
th:each=
"entry, stat : *{items}"
>
<div
th:each=
"entry, stat : *{items}"
>
<span
th:text=
"*{wineNames[__${entry.bottleId}__]}"
/>
<span
th:text=
"*{wineNames[__${entry.bottleId}__]}"
/>
<input
type=
"number"
id=
"quantity"
name=
"quantity"
min=
"1"
max=
"5"
th:field=
"*{quantities[__${entry.id}__]}"
>
<input
type=
"number"
id=
"quantity"
name=
"quantity"
min=
"1"
th:field=
"*{quantities[__${entry.id}__]}"
>
</div>
</div>
<button
type=
"submit"
class=
"btn btn-primary"
>
Submit
</button>
<button
type=
"submit"
class=
"btn btn-primary"
>
Submit
</button>
</form>
</form>
...
...
winery/webapp/src/main/resources/templates/winebottle/detail.html
View file @
ef752cef
...
@@ -42,7 +42,7 @@
...
@@ -42,7 +42,7 @@
</table>
</table>
<div
class=
"mb-2"
>
<div
class=
"mb-2"
>
<input
id=
"itemCount"
type=
"number"
value=
"1"
/>
<input
id=
"itemCount"
type=
"number"
min=
"1"
value=
"1"
/>
<a
class=
"btn btn-primary btn-sm"
href=
"#"
th:onclick=
"'addToCart(' + ${model.id} + ', document.getElementById(\'itemCount\').value).then(x => document.getElementById(\'success-alert\').style.display = \'block\')'"
>
Add to cart
</a>
<a
class=
"btn btn-primary btn-sm"
href=
"#"
th:onclick=
"'addToCart(' + ${model.id} + ', document.getElementById(\'itemCount\').value).then(x => document.getElementById(\'success-alert\').style.display = \'block\')'"
>
Add to cart
</a>
</div>
</div>
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment