Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
W
warehouse-management
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ascend
warehouse-management
Commits
88155b03
Commit
88155b03
authored
May 08, 2021
by
Alex Pinto
Browse files
Options
Browse Files
Download
Plain Diff
fixed merge conflicts
parents
5c290618
bb36c8dd
Changes
37
Hide whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
741 additions
and
242 deletions
+741
-242
.DS_Store
.DS_Store
+0
-0
package-lock.json
frontend/package-lock.json
+27
-24
favicon.ico
frontend/public/favicon.ico
+0
-0
index.html
frontend/public/index.html
+2
-2
logo192.png
frontend/public/logo192.png
+0
-0
logo512.png
frontend/public/logo512.png
+0
-0
manifest.json
frontend/public/manifest.json
+0
-10
filter.css
frontend/public/stylesheets/filter.css
+114
-0
footer.css
frontend/public/stylesheets/footer.css
+7
-0
header.css
frontend/public/stylesheets/header.css
+7
-0
master.css
frontend/public/stylesheets/master.css
+5
-0
order.css
frontend/public/stylesheets/order.css
+137
-0
reset.css
frontend/public/stylesheets/reset.css
+43
-0
App.jsx
frontend/src/App.jsx
+35
-9
order_actions.js
frontend/src/actions/order_actions.js
+3
-14
Button.jsx
frontend/src/components/atoms/Button.jsx
+7
-0
Error.jsx
frontend/src/components/atoms/Error.jsx
+7
-0
Input.jsx
frontend/src/components/atoms/Input.jsx
+12
-0
Select.jsx
frontend/src/components/atoms/Select.jsx
+18
-0
Filter.jsx
frontend/src/components/filter/Filter.jsx
+114
-0
FilterSearch.jsx
frontend/src/components/filter/FilterSearch.jsx
+16
-0
Search.jsx
frontend/src/components/filter/Search.jsx
+78
-0
Footer.jsx
frontend/src/components/footer/Footer.jsx
+5
-1
Header.jsx
frontend/src/components/header/Header.jsx
+7
-3
ItemDetails.jsx
frontend/src/components/order/ItemDetails.jsx
+12
-0
NoOrders.jsx
frontend/src/components/order/NoOrders.jsx
+6
-0
OrderButtons.jsx
frontend/src/components/order/OrderButtons.jsx
+6
-13
OrderDetails.jsx
frontend/src/components/order/OrderDetails.jsx
+21
-0
OrderIndex.jsx
frontend/src/components/order/OrderIndex.jsx
+2
-33
OrderIndexItem.jsx
frontend/src/components/order/OrderIndexItem.jsx
+19
-9
index.css
frontend/src/index.css
+0
-13
index.jsx
frontend/src/index.jsx
+0
-1
logo.svg
frontend/src/logo.svg
+0
-1
orders_reducer.js
frontend/src/reducers/entities/orders_reducer.js
+5
-9
orders_api_util.jsx
frontend/src/util/orders_api_util.jsx
+5
-100
WarehouseController.java
...nalproject/warehouse/controllers/WarehouseController.java
+12
-0
WarehouseOrderService.java
...inalproject/warehouse/services/WarehouseOrderService.java
+9
-0
No files found.
.DS_Store
View file @
88155b03
No preview for this file type
frontend/package-lock.json
View file @
88155b03
{
"name": "warehouse",
"version": "0.1.0",
"lockfileVersion":
2
,
"lockfileVersion":
1
,
"requires": true,
<<<<<<< HEAD
=======
"packages": {
"": {
"name": "warehouse",
...
...
@@ -19868,6 +19870,7 @@
}
}
},
>>>>>>> master
"dependencies": {
"@babel/code-frame": {
"version": "7.12.13",
...
...
@@ -34035,21 +34038,6 @@
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}
}
},
"string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
...
...
@@ -34106,6 +34094,21 @@
"define-properties": "^1.1.3"
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
}
}
},
"stringify-object": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
...
...
@@ -35948,14 +35951,6 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
...
...
@@ -35981,6 +35976,14 @@
}
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
frontend/public/favicon.ico
View replaced file @
5c290618
View file @
88155b03
3.78 KB
|
W:
|
H:
1.04 KB
|
W:
|
H:
2-up
Swipe
Onion skin
frontend/public/index.html
View file @
88155b03
...
...
@@ -9,7 +9,7 @@
name=
"description"
content=
"Web site created using create-react-app"
/>
<link
rel=
"
apple-touch-icon"
href=
"%PUBLIC_URL%/logo192.png"
/
>
<link
rel=
"
stylesheet"
href=
"./stylesheets/master.css"
>
<!--
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/
...
...
@@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>
React App
</title>
<title>
Ascend Warehouse
</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
...
...
frontend/public/logo192.png
deleted
100644 → 0
View file @
5c290618
5.22 KB
frontend/public/logo512.png
deleted
100644 → 0
View file @
5c290618
9.44 KB
frontend/public/manifest.json
View file @
88155b03
...
...
@@ -6,16 +6,6 @@
"src"
:
"favicon.ico"
,
"sizes"
:
"64x64 32x32 24x24 16x16"
,
"type"
:
"image/x-icon"
},
{
"src"
:
"logo192.png"
,
"type"
:
"image/png"
,
"sizes"
:
"192x192"
},
{
"src"
:
"logo512.png"
,
"type"
:
"image/png"
,
"sizes"
:
"512x512"
}
],
"start_url"
:
"."
,
...
...
frontend/public/stylesheets/filter.css
0 → 100644
View file @
88155b03
.filter-search
{
/* width: 50%; */
display
:
flex
;
align-items
:
center
;
padding
:
20px
;
}
.filter-search
.text
{
font-size
:
20px
;
font-weight
:
700
;
}
.filter-search
>
*
~
*
{
margin-left
:
100px
;
}
.filter
{
display
:
flex
;
align-items
:
center
;
}
.filter
>
*
~
*
{
margin-left
:
10px
;
}
.filter-btns
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.filter-btns
>
*
{
cursor
:
pointer
;
display
:
flex
;
padding
:
10px
20px
;
border-radius
:
5px
;
border
:
1px
solid
black
;
}
.filter-btns
>
*
~
*
{
margin-left
:
10px
;
}
.selected
{
color
:
white
;
}
.filter-all
:hover
{
background
:
orange
;
color
:
white
;
}
.filter-all.selected
{
background
:
orange
;
}
.filter-rec
:hover
{
background
:
blue
;
color
:
white
;
}
.filter-rec.selected
{
background
:
blue
;
}
.filter-ful
:hover
{
background
:
green
;
color
:
white
;
}
.filter-ful.selected
{
background
:
green
;
}
.filter-can
:hover
{
background
:
red
;
color
:
white
;
}
.filter-can.selected
{
background
:
red
;
}
.search
{
display
:
flex
;
align-items
:
center
;
}
.search
>
*
~
*
{
margin-left
:
10px
;
}
.search
>
select
{
cursor
:
pointer
;
border
:
none
;
font-size
:
16px
;
font-family
:
'Times New Roman'
,
Times
,
serif
;
}
.search
>
input
{
padding
:
10px
;
font-size
:
16px
;
font-family
:
'Times New Roman'
,
Times
,
serif
;
}
.search-btn
{
cursor
:
pointer
;
display
:
flex
;
padding
:
10px
20px
;
border-radius
:
5px
;
border
:
1px
solid
black
;
}
.search-btn
:hover
{
color
:
white
;
background
:
gray
;
}
.error
{
color
:
red
;
}
\ No newline at end of file
frontend/public/stylesheets/footer.css
0 → 100644
View file @
88155b03
.footer
{
display
:
flex
;
align-items
:
center
;
height
:
50px
;
background
:
gray
;
color
:
white
;
}
\ No newline at end of file
frontend/public/stylesheets/header.css
0 → 100644
View file @
88155b03
.header
{
display
:
flex
;
align-items
:
center
;
height
:
50px
;
background
:
gray
;
color
:
white
;
}
\ No newline at end of file
frontend/public/stylesheets/master.css
0 → 100644
View file @
88155b03
@import
'./reset.css'
;
@import
'./header.css'
;
@import
'./footer.css'
;
@import
'./filter.css'
;
@import
'./order.css'
;
\ No newline at end of file
frontend/public/stylesheets/order.css
0 → 100644
View file @
88155b03
.order-index
{
padding
:
0
20px
20px
;
}
.order-index
>
h1
{
padding
:
10px
20px
;
margin-bottom
:
2px
;
background
:
blue
;
color
:
white
;
font-size
:
20px
;
}
.order-index
.oii
~
.oii
{
margin-top
:
15px
;
}
.oii
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
min-height
:
70px
;
background
:
lightgray
;
border-radius
:
5px
;
}
.oii-container
{
width
:
100%
;
display
:
flex
;
justify-content
:
space-between
;
padding
:
15px
;
box-sizing
:
border-box
;
}
.oii-left
{
display
:
flex
;
align-items
:
center
;
}
.oii-drop
{
transition
:
transform
1s
;
cursor
:
pointer
;
margin-right
:
10px
;
padding
:
5px
;
font-size
:
20px
;
background
:
white
;
border-radius
:
50%
;
}
.oii-drop.rotate
{
transition
:
transform
1s
;
transform
:
rotate
(
90deg
);
}
.oii-num
{
display
:
flex
;
align-items
:
center
;
}
.oii-buttons
,
.oii-status
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
width
:
180px
;
}
.oii-buttons
>
div
{
cursor
:
pointer
;
display
:
flex
;
align-items
:
center
;
background
:
white
;
border-radius
:
5px
;
padding
:
10px
20px
;
}
.oii-buttons
>
*
~
*
{
margin-left
:
10px
;
}
.oii-status.fulfilled
{
color
:
green
;
}
.oii-status.cancelled
{
color
:
red
;
}
.fulfill-btn
{
position
:
relative
;
border
:
1px
solid
green
;
color
:
green
;
box-shadow
:
inset
0
1px
0
rgba
(
255
,
255
,
255
,
0.4
),
0
1px
1px
rgba
(
0
,
0
,
0
,
0.5
);
}
.fulfill-btn
:hover
{
color
:
white
;
background
:
green
;
box-shadow
:
none
;
}
.cancel-btn
{
position
:
relative
;
border
:
1px
solid
red
;
color
:
red
;
box-shadow
:
inset
0
1px
0
rgba
(
255
,
255
,
255
,
0.4
),
0
1px
1px
rgba
(
0
,
0
,
0
,
0.5
);
}
.cancel-btn
:hover
{
color
:
white
;
background
:
red
;
}
.fulfill-btn
:active
,
.cancel-btn
:active
{
top
:
2px
;
}
.order-details
{
display
:
flex
;
flex-direction
:
column
;
/* min-height: 0px; */
max-height
:
0px
;
overflow
:
hidden
;
transition
:
max-height
1s
ease-out
;
}
.order-details-container
{
overflow
:
hidden
;
padding
:
0
50px
20px
;
box-sizing
:
border-box
;
}
.animate
{
transition
:
max-height
1s
ease-out
;
max-height
:
200px
;
}
.item-detail
~
.item-detail
{
margin-top
:
10px
;
}
\ No newline at end of file
frontend/public/stylesheets/reset.css
0 → 100644
View file @
88155b03
html
,
body
,
div
,
span
,
applet
,
object
,
iframe
,
h1
,
h2
,
h3
,
h4
,
h5
,
h6
,
p
,
blockquote
,
pre
,
a
,
abbr
,
acronym
,
address
,
big
,
cite
,
code
,
del
,
dfn
,
em
,
img
,
ins
,
kbd
,
q
,
s
,
samp
,
small
,
strike
,
strong
,
sub
,
sup
,
tt
,
var
,
b
,
u
,
i
,
center
,
dl
,
dt
,
dd
,
ol
,
ul
,
li
,
fieldset
,
form
,
label
,
legend
,
table
,
caption
,
tbody
,
tfoot
,
thead
,
tr
,
th
,
td
,
article
,
aside
,
canvas
,
details
,
embed
,
figure
,
figcaption
,
footer
,
header
,
hgroup
,
menu
,
nav
,
output
,
ruby
,
section
,
summary
,
time
,
mark
,
audio
,
video
{
margin
:
0
;
padding
:
0
;
border
:
0
;
font-size
:
100%
;
font
:
inherit
;
vertical-align
:
baseline
;
}
/* HTML5 display-role reset for older browsers */
article
,
aside
,
details
,
figcaption
,
figure
,
footer
,
header
,
hgroup
,
menu
,
nav
,
section
{
display
:
block
;
}
body
{
line-height
:
1
;
}
ol
,
ul
{
list-style
:
none
;
}
blockquote
,
q
{
quotes
:
none
;
}
blockquote
:before
,
blockquote
:after
,
q
:before
,
q
:after
{
content
:
''
;
content
:
none
;
}
table
{
border-collapse
:
collapse
;
border-spacing
:
0
;
}
\ No newline at end of file
frontend/src/App.jsx
View file @
88155b03
import
Login
from
"./components/session/Login
"
;
import
Logout
from
"./components/session/Logout
"
;
import
{
useEffect
,
useState
}
from
"react
"
;
import
{
connect
}
from
"react-redux
"
;
import
Header
from
"./components/header/Header"
;
import
Footer
from
"./components/footer/Footer"
;
import
OrderIndex
from
"./components/order/OrderIndex"
import
NoOrders
from
"./components/order/NoOrders"
;
import
{
fetchOrders
}
from
"./actions/order_actions"
;
import
FilterSearch
from
"./components/filter/FilterSearch"
;
const
App
=
({
orders
,
fetchOrders
})
=>
{
const
[
ordersToShow
,
setOrdersToShow
]
=
useState
(
orders
);
const
[
fetchAttempted
,
setFetchAttempted
]
=
useState
(
false
);
// debugger
useEffect
(()
=>
{
if
(
!
fetchAttempted
)
{
fetchOrders
();
setFetchAttempted
(
true
);
}
setOrdersToShow
(
orders
);
},
[
orders
,
fetchOrders
,
fetchAttempted
]);
const
App
=
()
=>
{
return
(
<
div
className=
"app"
>
<
Login
/>
<
Logout
/>
<
Header
/>
<
OrderIndex
/>
<
Footer
/>
<
FilterSearch
setOrdersToShow=
{
setOrdersToShow
}
/>
{
ordersToShow
.
allIds
.
length
?
(
<
OrderIndex
orders=
{
ordersToShow
}
/>
)
:
(
<
NoOrders
/>
)
}
</
div
>
);
}
export
default
App
;
const
mapStateToProps
=
(
state
)
=>
({
orders
:
state
.
entities
.
orders
,
});
const
mapDispatchToProps
=
(
dispatch
)
=>
({
fetchOrders
:
()
=>
dispatch
(
fetchOrders
()),
});
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
App
);
frontend/src/actions/order_actions.js
View file @
88155b03
import
*
as
OrderAPIUtil
from
'../util/orders_api_util'
;
export
const
RECEIVE_ORDERS
=
'RECEIVE_ORDERS'
;
export
const
RECEIVE_ORDER
=
'RECEIVE_ORDER'
;
export
const
UPDATE_ORDER
=
'UPDATE_ORDER'
;
const
receiveOrders
=
(
orders
)
=>
({
...
...
@@ -9,11 +8,6 @@ const receiveOrders = (orders) => ({
orders
,
})
const
receiveOrder
=
(
order
)
=>
({
type
:
RECEIVE_ORDER
,
order
,
})
const
updateOrder
=
(
order
)
=>
({
type
:
UPDATE_ORDER
,
order
,
...
...
@@ -22,19 +16,14 @@ const updateOrder = (order) => ({
export
const
fetchOrders
=
()
=>
(
dispatch
)
=>
{
OrderAPIUtil
.
getOrders
()
.
then
(
res
=>
{
dispatch
(
receiveOrders
(
res
))
dispatch
(
receiveOrders
(
res
.
data
))
});
}
export
const
createOrder
=
(
order
)
=>
(
dispatch
)
=>
OrderAPIUtil
.
createOrder
(
order
)
.
then
(
res
=>
{
dispatch
(
receiveOrder
(
res
))
});
export
const
editOrder
=
(
order
)
=>
(
dispatch
)
=>
{
OrderAPIUtil
.
editOrder
(
order
)
.
then
(
res
=>
{
dispatch
(
updateOrder
(
res
))
debugger
dispatch
(
updateOrder
(
res
.
data
))
});
}
\ No newline at end of file
frontend/src/components/atoms/Button.jsx
0 → 100644
View file @
88155b03
const
Button
=
({
className
,
onClick
,
text
})
=>
(
<
div
className=
{
className
}
onClick=
{
onClick
}
>
{
text
}
</
div
>
)
export
default
Button
;
\ No newline at end of file
frontend/src/components/atoms/Error.jsx
0 → 100644
View file @
88155b03
const
Error
=
({
text
,
className
})
=>
(
<
div
className=
{
`error ${className || ""}`
}
>
{
text
}
</
div
>
)
export
default
Error
;
\ No newline at end of file
frontend/src/components/atoms/Input.jsx
0 → 100644
View file @
88155b03
const
Input
=
({
className
,
type
,
placeholder
,
value
,
onChange
,
onKeyPress
})
=>
(
<
input
type=
{
type
||
"text"
}
className=
{
className
}
placeholder=
{
placeholder
}
value=
{
value
}
onChange=
{
onChange
}
onKeyPress=
{
onKeyPress
}
/>
);
export
default
Input
;
frontend/src/components/atoms/Select.jsx
0 → 100644
View file @
88155b03
const
Select
=
({
defaultVal
,
value
,
onChange
,
options
})
=>
(
<
select
value=
{
value
?
value
:
defaultVal
}
onChange=
{
onChange
}
>
{
defaultVal
?
(
<
option
disabled
>
{
defaultVal
}
</
option
>
)
:
null
}
{
options
.
map
(
option
=>
(
<
option
key=
{
option
}
value=
{
option
}
>
{
option
}
</
option
>
))
}
</
select
>
)
export
default
Select
;
\ No newline at end of file
frontend/src/components/filter/Filter.jsx
0 → 100644
View file @
88155b03
import
{
useEffect
,
useState
}
from
"react"
;
import
{
connect
}
from
"react-redux"
;
import
Button
from
"../atoms/Button"
;
const
Filter
=
({
orders
,
filtersOn
,
setOrdersToShow
})
=>
{
const
[
all
,
setAll
]
=
useState
(
false
);
const
[
received
,
setReceived
]
=
useState
(
false
);
const
[
fulfilled
,
setFulfilled
]
=
useState
(
false
);
const
[
cancelled
,
setCancelled
]
=
useState
(
false
);
const
RECEIVED
=
"RECEIVED"
;
const
FULFILLED
=
"FULFILLED"
;
const
CANCELLED
=
"CANCELLED"
;
const
reset
=
()
=>
{
setAll
(
true
);
setReceived
(
false
);
setFulfilled
(
false
);
setCancelled
(
false
);
};
const
receive
=
()
=>
{
setAll
(
false
);
setReceived
(
!
received
);
if
(
received
&&
!
fulfilled
&&
!
cancelled
)
{
setAll
(
true
);
}
};
const
fulfill
=
()
=>
{
setAll
(
false
);
setFulfilled
(
!
fulfilled
);
if
(
!
received
&&
fulfilled
&&
!
cancelled
)
{
setAll
(
true
);
}
};
const
cancel
=
()
=>
{
setAll
(
false
);
setCancelled
(
!
cancelled
);
if
(
!
received
&&
!
fulfilled
&&
cancelled
)
{
setAll
(
true
);
}
};
useEffect
(()
=>
{
if
(
all
||
(
!
all
&&
!
received
&&
!
fulfilled
&&
!
cancelled
))
{
setOrdersToShow
(
orders
);
}
else
{
const
newOrders
=
{
allIds
:
[],
byId
:
{}
};
orders
.
allIds
.
forEach
((
wareId
)
=>
{
const
order
=
orders
.
byId
[
wareId
];
switch
(
order
.
status
)
{
case
RECEIVED
:
if
(
all
||
received
)
{
newOrders
.
allIds
.
push
(
wareId
);
newOrders
.
byId
[
wareId
]
=
order
;
}
break
;
case
FULFILLED
:
if
(
all
||
fulfilled
)
{
newOrders
.
allIds
.
push
(
wareId
);
newOrders
.
byId
[
wareId
]
=
order
;
}
break
;
case
CANCELLED
:
if
(
all
||
cancelled
)
{
newOrders
.
allIds
.
push
(
wareId
);
newOrders
.
byId
[
wareId
]
=
order
;
}
break
;
default
:
break
;
}
});
setOrdersToShow
(
newOrders
);
}
},
[
orders
,
setOrdersToShow
,
all
,
received
,
fulfilled
,
cancelled
]);
if
(
!
orders
.
allIds
.
length
)
return
null
;
return
(
<
div
className=
"filter"
>
<
div
className=
"text"
>
Filter:
</
div
>
<
div
className=
"filter-btns"
>
<
Button
className=
{
`filter-all ${filtersOn && all ? "selected" : ""}`
}
onClick=
{
reset
}
text=
"All"
/>
<
Button
className=
{
`filter-rec ${filtersOn && received ? "selected" : ""}`
}
onClick=
{
receive
}
text=
"Received"
/>
<
Button
className=
{
`filter-ful ${filtersOn && fulfilled ? "selected" : ""}`
}
onClick=
{
fulfill
}
text=
"Fulfilled"
/>
<
Button
className=
{
`filter-can ${filtersOn && cancelled ? "selected" : ""}`
}
onClick=
{
cancel
}
text=
"Cancelled"
/>
</
div
>
</
div
>
);
};
const
mapStateToProps
=
(
state
)
=>
({
orders
:
state
.
entities
.
orders
,
});
const
mapDispatchToProps
=
(
dispatch
)
=>
({});
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
Filter
);
frontend/src/components/filter/FilterSearch.jsx
0 → 100644
View file @
88155b03
import
{
useEffect
,
useState
}
from
"react"
;
import
Filter
from
"./Filter"
;
import
Search
from
"./Search"
;
const
FilterSearch
=
({
orders
,
setOrdersToShow
})
=>
{
const
[
filtersOn
,
setFiltersOn
]
=
useState
(
true
);
return
(
<
div
className=
"filter-search"
>
<
Filter
filtersOn=
{
filtersOn
}
setOrdersToShow=
{
setOrdersToShow
}
/>
<
Search
setOrdersToShow=
{
setOrdersToShow
}
setFiltersOn=
{
setFiltersOn
}
/>
</
div
>
);
};
export
default
FilterSearch
;
frontend/src/components/filter/Search.jsx
0 → 100644
View file @
88155b03
import
{
useState
}
from
"react"
;
import
{
connect
}
from
"react-redux"
;
import
Select
from
"../atoms/Select"
;
import
Input
from
"../atoms/Input"
;
import
Button
from
"../atoms/Button"
;
import
Error
from
"../atoms/Error"
;
const
Search
=
({
orders
,
setOrdersToShow
,
setFiltersOn
})
=>
{
const
[
searchInput
,
setSearchInput
]
=
useState
(
""
);
const
[
searchBy
,
setSearchBy
]
=
useState
(
""
);
const
[
error
,
setError
]
=
useState
(
""
);
const
searchOptions
=
[
"by Warehouse ID"
,
"by Order ID"
];
const
search
=
()
=>
{
if
(
!
searchInput
.
length
)
{
setError
(
"Please enter a search parameter."
);
}
else
if
(
!
searchBy
.
length
)
{
setError
(
"Please enter search method."
);
}
else
{
const
searchResult
=
{
allIds
:
[],
byId
:
{}
};
const
searchedOrder
=
searchBy
===
searchOptions
[
0
]
?
orders
.
byId
[
searchInput
]
:
searchBy
===
searchOptions
[
1
]
?
orders
.
byOrderId
[
searchInput
]
:
null
;
if
(
searchedOrder
)
{
searchResult
.
allIds
.
push
(
searchedOrder
.
id
);
searchResult
.
byId
[
searchedOrder
.
id
]
=
searchedOrder
;
}
if
(
searchBy
.
length
)
{
setOrdersToShow
(
searchResult
);
}
setError
(
""
);
setSearchInput
(
""
);
setFiltersOn
(
false
);
}
};
const
handleSearchEnter
=
(
e
)
=>
{
if
(
e
.
key
===
"Enter"
)
{
search
();
}
};
return
(
<
div
className=
"search"
>
<
div
className=
"text"
>
Search
</
div
>
<
Select
defaultVal=
{
"By:"
}
value=
{
searchBy
}
options=
{
searchOptions
}
onChange=
{
(
e
)
=>
{
setSearchBy
(
e
.
target
.
selectedOptions
[
0
].
value
);
}
}
/>
<
Input
placeholder=
{
"Search by ID"
}
value=
{
searchInput
}
onChange=
{
(
e
)
=>
setSearchInput
(
e
.
target
.
value
)
}
onKeyPress=
{
handleSearchEnter
}
/>
<
Button
className=
"search-btn"
text=
"Search"
onClick=
{
search
}
/>
{
error
.
length
?
<
Error
text=
{
error
}
/>
:
null
}
</
div
>
);
};
const
mapStateToProps
=
(
state
)
=>
({
orders
:
state
.
entities
.
orders
,
});
const
mapDispatchToProps
=
(
dispatch
)
=>
({
});
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
Search
);
frontend/src/components/footer/Footer.jsx
View file @
88155b03
import
React
from
"react"
;
const
Footer
=
()
=>
{
return
<
div
>
Footer
</
div
>;
return
(
<
div
className=
"footer"
>
Footer
</
div
>
);
};
export
default
Footer
;
frontend/src/components/header/Header.jsx
View file @
88155b03
import
React
from
'react'
;
import
Login
from
"../session/Login"
;
import
Logout
from
"../session/Logout"
;
const
Header
=
()
=>
{
return
(
<
div
>
Header
<
div
className=
"header"
>
Warehouse Management
<
Login
/>
<
Logout
/>
</
div
>
)
)
;
}
export
default
Header
;
\ No newline at end of file
frontend/src/components/order/ItemDetails.jsx
0 → 100644
View file @
88155b03
const
ItemDetails
=
({
item
})
=>
{
const
{
itemId
,
itemName
,
itemQuantity
,
itemPrice
,
itemSku
}
=
item
;
return
(
<
li
className=
"item-detail"
>
{
`${itemName} -- x ${itemQuantity} bought at ${itemPrice}/ea.`
}
</
li
>
)
}
export
default
ItemDetails
;
\ No newline at end of file
frontend/src/components/order/NoOrders.jsx
0 → 100644
View file @
88155b03
const
NoOrders
=
()
=>
(
<
div
>
No Orders To Show
</
div
>
)
export
default
NoOrders
;
\ No newline at end of file
frontend/src/components/order/OrderButtons.jsx
View file @
88155b03
import
React
from
"react"
;
import
{
connect
}
from
"react-redux"
;
import
{
editOrder
}
from
"../../actions/order_actions"
;
import
Button
from
"../atoms/Button"
;
const
OrderButtons
=
({
order
,
editOrder
})
=>
{
// const { id, orderId, status, orderItems, createdAt, modifiedAt } = order;
const
handleUpdate
=
(
action
)
=>
{
console
.
log
(
action
);
if
(
action
===
"FULFILL"
)
{
editOrder
({
...
order
,
status
:
"FULFILLED"
})
editOrder
({
...
order
,
status
:
"FULFILLED"
});
}
else
if
(
action
===
"CANCEL"
)
{
editOrder
({
...
order
,
status
:
"CANCELLED"
});
}
}
const
fulfill
=
(
<
button
onClick=
{
()
=>
handleUpdate
(
"FULFILL"
)
}
>
Fulfill
</
button
>
)
};
const
cancel
=
(
<
button
onClick=
{
()
=>
handleUpdate
(
"CANCEL"
)
}
>
Cancel
</
button
>
)
return
(
<
div
>
{
fulfill
}
{
cancel
}
<
div
className=
"oii-buttons"
>
<
Button
className=
"fulfill-btn"
onClick=
{
()
=>
handleUpdate
(
"FULFILL"
)
}
text=
"Fulfill"
/>
<
Button
className=
"cancel-btn"
onClick=
{
()
=>
handleUpdate
(
"CANCEL"
)
}
text=
"Cancel"
/>
</
div
>
);
};
...
...
frontend/src/components/order/OrderDetails.jsx
0 → 100644
View file @
88155b03
import
ItemDetails
from
"./ItemDetails"
;
const
OrderDetails
=
({
order
,
showDetails
})
=>
{
return
(
<
div
className=
{
`order-details ${showDetails ? "animate" : ""}`
}
>
<
div
className=
"order-details-container"
>
<
div
>
{
`Order Details - Warehouse Order #: ${order.id}`
}
</
div
>
<
ul
>
{
order
.
orderItems
.
map
(
item
=>
(
<
ItemDetails
key=
{
item
.
itemId
}
item=
{
item
}
/>
))
}
</
ul
>
</
div
>
</
div
>
)
}
export
default
OrderDetails
;
\ No newline at end of file
frontend/src/components/order/OrderIndex.jsx
View file @
88155b03
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
OrderIndexItem
from
'./OrderIndexItem'
;
import
{
connect
}
from
"react-redux"
;
import
{
createOrder
,
fetchOrders
,
editOrder
}
from
'../../actions/order_actions'
;
const
OrderIndex
=
({
orders
,
fetchOrders
,
createOrder
,
editOrder
,
})
=>
{
const
[
fetchAttempted
,
setFetchAttempted
]
=
useState
(
false
);
useEffect
(()
=>
{
if
(
!
fetchAttempted
)
{
setFetchAttempted
(
true
);
fetchOrders
();
}
},
[
fetchOrders
,
fetchAttempted
])
return
(
<
div
>
<
div
className=
"order-index"
>
<
h1
>
Order Index
</
h1
>
<
button
onClick=
{
createOrder
}
>
Create New Order
</
button
>
<
button
onClick=
{
editOrder
}
>
Update Order
</
button
>
<
br
/>
<
br
/>
{
orders
.
allIds
.
map
((
orderId
)
=>
{
const
order
=
orders
.
byId
[
orderId
];
return
<
OrderIndexItem
key=
{
order
.
id
}
order=
{
order
}
/>;
...
...
@@ -36,14 +15,4 @@ const OrderIndex = ({
}
const
mapStateToProps
=
(
state
)
=>
({
orders
:
state
.
entities
.
orders
,
})
const
mapDispatchToProps
=
dispatch
=>
({
fetchOrders
:
()
=>
dispatch
(
fetchOrders
()),
createOrder
:
order
=>
dispatch
(
createOrder
(
order
)),
editOrder
:
order
=>
dispatch
(
editOrder
(
order
)),
})
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
OrderIndex
)
export
default
OrderIndex
;
\ No newline at end of file
frontend/src/components/order/OrderIndexItem.jsx
View file @
88155b03
import
React
from
'react'
;
import
React
,
{
useState
}
from
'react'
;
import
OrderButtons
from
'./OrderButtons'
;
import
{
FiChevronRight
}
from
'react-icons/fi'
;
import
OrderDetails
from
'./OrderDetails'
;
const
OrderIndexItem
=
({
order
})
=>
{
...
...
@@ -12,16 +14,24 @@ const OrderIndexItem = ({ order }) => {
modifiedAt
}
=
order
;
const
actions
=
(
status
===
"RECEIVED"
?
<
OrderButtons
order=
{
order
}
/>
:
<
div
>
{
status
}
</
div
>)
const
[
showDetails
,
setShowDetails
]
=
useState
(
false
);
const
actions
=
(
status
===
"FULFILLED"
||
status
===
"CANCELLED"
?
<
div
className=
{
`oii-status ${status.toLowerCase()}`
}
>
{
status
}
</
div
>
:
<
OrderButtons
order=
{
order
}
/>
);
return
(
<
div
>
<
div
>
{
`Order #: ${orderId}`
}
</
div
>
{
actions
}
<
div
className=
"oii"
>
<
div
className=
"oii-container"
>
<
div
className=
"oii-left"
>
<
FiChevronRight
className=
{
`oii-drop ${showDetails ? "rotate" : ""}`
}
onClick=
{
()
=>
setShowDetails
(
!
showDetails
)
}
/>
<
div
className=
"oii-num"
>
{
`Order #: ${orderId}`
}
</
div
>
</
div
>
{
actions
}
</
div
>
<
OrderDetails
showDetails=
{
showDetails
}
order=
{
order
}
/>
</
div
>
)
};
...
...
frontend/src/index.css
deleted
100644 → 0
View file @
5c290618
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
;
}
frontend/src/index.jsx
View file @
88155b03
import
React
from
'react'
;
import
ReactDOM
from
'react-dom'
;
import
'./index.css'
;
import
App
from
'./App'
;
import
reportWebVitals
from
'./reportWebVitals'
;
import
configureStore
from
'./store/store'
;
...
...
frontend/src/logo.svg
deleted
100644 → 0
View file @
5c290618
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 841.9 595.3"
><g
fill=
"#61DAFB"
><path
d=
"M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"
/><circle
cx=
"420.9"
cy=
"296.5"
r=
"45.7"
/><path
d=
"M520.5 78.1z"
/></g></svg>
\ No newline at end of file
frontend/src/reducers/entities/orders_reducer.js
View file @
88155b03
import
{
RECEIVE_ORDER
,
RECEIVE_ORDERS
,
UPDATE_ORDER
,
}
from
"../../actions/order_actions"
;
...
...
@@ -13,22 +12,19 @@ const OrdersReducer = (oldState = initialState, action) => {
Object
.
freeze
(
oldState
);
const
newState
=
{
...
oldState
};
const
order
=
action
.
order
||
null
;
switch
(
action
.
type
)
{
case
RECEIVE_ORDERS
:
const
orderState
=
{
byId
:
{},
allIds
:
[],
byOrderId
:
{},
};
Object
.
keys
(
action
.
orders
).
forEach
(
wareId
=>
{
orderState
.
allIds
.
push
(
wareId
);
orderState
.
byId
[
wareId
]
=
action
.
orders
[
wareId
];
action
.
orders
.
forEach
(
order
=>
{
orderState
.
allIds
.
push
(
order
.
id
);
orderState
.
byId
[
order
.
id
]
=
order
;
orderState
.
byOrderId
[
order
.
orderId
]
=
order
;
})
return
orderState
;
case
RECEIVE_ORDER
:
newState
.
byId
[
order
.
id
]
=
order
;
newState
.
allIds
.
push
(
order
.
id
);
return
newState
;
case
UPDATE_ORDER
:
newState
.
byId
[
order
.
id
]
=
order
;
return
newState
;
...
...
frontend/src/util/orders_api_util.jsx
View file @
88155b03
//
import axios from 'axios';
import
axios
from
'axios'
;
const
RECEIVED
=
"RECEIVED"
;
// const FULFILLED = "FULFILLED";
// const CANCELLED = "CANCELLED";
const
sampleGetAll
=
{
// allIds: ["1", "2", "3"],
// byId: {
1
:
{
id
:
"1"
,
orderId
:
"o1"
,
status
:
RECEIVED
,
orderItems
:
[
{
itemId
:
"17"
,
itemName
:
"Item 1"
,
itemQuantity
:
4
,
itemPrice
:
17.99
,
itemSku
:
8765309
,
},
{
itemId
:
"18"
,
itemName
:
"Item 2"
,
itemQuantity
:
42
,
itemPrice
:
17.99
,
itemSku
:
8715309
,
},
],
},
2
:
{
id
:
"2"
,
orderId
:
"o2"
,
status
:
RECEIVED
,
orderItems
:
[
{
itemId
:
"17"
,
itemName
:
"Item 1"
,
itemQuantity
:
3
,
itemPrice
:
17.99
,
itemSku
:
8765309
,
},
{
itemId
:
"18"
,
itemName
:
"Item 2"
,
itemQuantity
:
41
,
itemPrice
:
17.99
,
itemSku
:
8715309
,
},
],
},
3
:
{
id
:
"3"
,
orderId
:
"o3"
,
status
:
RECEIVED
,
orderItems
:
[
{
itemId
:
"17"
,
itemName
:
"Item 1"
,
itemQuantity
:
2
,
itemPrice
:
17.99
,
itemSku
:
8765309
,
},
{
itemId
:
"18"
,
itemName
:
"Item 2"
,
itemQuantity
:
40
,
itemPrice
:
17.99
,
itemSku
:
8715309
,
},
],
},
// },
};
const
sampleNew
=
{
id
:
"4"
,
orderId
:
"o4"
,
status
:
RECEIVED
,
};
const
getAllPromise
=
new
Promise
(
(
resolve
,
reject
)
=>
{
resolve
(
sampleGetAll
)
})
const
createNewPromise
=
new
Promise
(
(
resolve
,
reject
)
=>
{
resolve
(
sampleNew
)
})
export
const
getOrders
=
()
=>
{
return
getAllPromise
;
};
export
const
createOrder
=
(
order
)
=>
{
return
createNewPromise
;
export
const
getOrders
=
()
=>
{
return
axios
.
get
(
`http://localhost:8080/api/orders`
);
}
export
const
editOrder
=
(
order
)
=>
{
return
new
Promise
(
(
resolve
,
reject
)
=>
{
resolve
(
order
);
})
debugger
;
return
axios
.
put
(
`http://localhost:8080/api/orders/
${
order
.
id
}
`
,
order
)
}
// export const getOrders =() => {
// return axios.get("http://localhost:8080/api/orders");
// }
\ No newline at end of file
src/main/java/com/ascendfinalproject/warehouse/controllers/WarehouseController.java
View file @
88155b03
...
...
@@ -42,5 +42,17 @@ public class WarehouseController {
return
orderService
.
updateOrder
(
order
,
id
);
}
@CrossOrigin
@DeleteMapping
(
value
=
"/orders/{id}"
)
public
Mono
<
Void
>
deleteOrder
(
@PathVariable
(
value
=
"id"
)
String
id
)
{
return
orderService
.
deleteOrder
(
id
);
}
@CrossOrigin
@DeleteMapping
(
value
=
"/orders/all"
)
public
Mono
<
Void
>
deleteAllOrders
()
{
System
.
out
.
println
(
"Deleted all orders"
);
return
orderService
.
deleteAllOrders
();
}
}
src/main/java/com/ascendfinalproject/warehouse/services/WarehouseOrderService.java
View file @
88155b03
...
...
@@ -49,4 +49,13 @@ public class WarehouseOrderService {
});
}
public
Mono
<
Void
>
deleteOrder
(
String
id
)
{
return
orderRepository
.
deleteById
(
id
);
}
public
Mono
<
Void
>
deleteAllOrders
()
{
return
orderRepository
.
deleteAll
();
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a 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