Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
S
shop-front
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
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
Kulpybaev Ilyas
shop-front
Commits
c6a0358f
Commit
c6a0358f
authored
Apr 25, 2024
by
Kulpybaev Ilyas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Урок 89
parent
7d5d1079
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
131 additions
and
30 deletions
+131
-30
.gitkeep
src/assets/.gitkeep
+0
-0
Image_not_available.png
src/assets/images/Image_not_available.png
+0
-0
axiosApi.ts
src/axiosApi.ts
+2
-1
.gitkeep
src/components/.gitkeep
+0
-0
ProductForm.tsx
src/components/ProductForm/ProductForm.tsx
+31
-13
ProductItem.tsx
src/components/ProductItem/ProductItem.tsx
+18
-3
FileInput.tsx
src/components/UI/Form/FileInput/FileInput.tsx
+63
-0
constants.ts
src/constants.ts
+1
-0
NewProduct.tsx
src/containers/Products/NewProduct.tsx
+1
-2
Products.tsx
src/containers/Products/Products.tsx
+11
-8
productsSlice.ts
src/features/productsSlice.ts
+2
-2
IProduct.ts
src/interfaces/IProduct.ts
+2
-1
No files found.
src/assets/.gitkeep
deleted
100644 → 0
View file @
7d5d1079
src/assets/images/Image_not_available.png
0 → 100644
View file @
c6a0358f
19.8 KB
src/axiosApi.ts
View file @
c6a0358f
import
axios
from
"axios"
;
import
{
apiURL
}
from
"./constants.ts"
;
const
axiosApi
=
axios
.
create
({
baseURL
:
"http://localhost:8000"
,
baseURL
:
apiURL
,
});
export
default
axiosApi
;
src/components/.gitkeep
deleted
100644 → 0
View file @
7d5d1079
src/components/ProductForm/ProductForm.tsx
View file @
c6a0358f
import
{
ChangeEvent
,
FormEvent
,
useState
}
from
"react"
;
import
{
Box
,
Button
,
Grid
,
TextField
}
from
"@mui/material"
;
import
{
CreateProductType
}
from
"../../interfaces/IProduct.ts
"
;
import
FileInput
from
"../UI/Form/FileInput/FileInput.tsx
"
;
interface
IState
{
nam
e
:
string
;
titl
e
:
string
;
price
:
string
;
description
:
string
;
image
:
string
;
}
interface
IProps
{
onSubmit
:
(
data
:
CreateProductType
)
=>
void
;
onSubmit
:
(
data
:
FormData
)
=>
void
;
}
const
ProductForm
=
({
onSubmit
}:
IProps
)
=>
{
const
[
state
,
setState
]
=
useState
<
IState
>
({
nam
e
:
""
,
titl
e
:
""
,
price
:
""
,
description
:
""
,
image
:
""
,
});
const
submitFormHandler
=
(
e
:
FormEvent
<
HTMLFormElement
>
)
=>
{
e
.
preventDefault
();
const
newProduct
:
CreateProductType
=
{
name
:
state
.
name
,
description
:
state
.
description
,
price
:
parseInt
(
state
.
price
),
};
const
formData
:
FormData
=
new
FormData
();
onSubmit
(
newProduct
);
Object
.
keys
(
state
).
forEach
((
key
)
=>
{
formData
.
append
(
key
,
state
[
key
as
keyof
IState
]);
});
onSubmit
(
formData
);
};
const
inputChangeHandler
=
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
...
...
@@ -37,6 +39,18 @@ const ProductForm = ({ onSubmit }: IProps) => {
});
};
const
fileChangeHandler
=
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
name
=
e
.
target
.
name
;
if
(
e
.
target
.
files
)
{
const
file
=
e
.
target
.
files
[
0
];
setState
((
prevState
)
=>
({
...
prevState
,
[
name
]:
file
,
}));
}
};
return
(
<
Box
component=
"form"
...
...
@@ -49,11 +63,11 @@ const ProductForm = ({ onSubmit }: IProps) => {
<
TextField
fullWidth
variant=
"outlined"
id=
"
nam
e"
id=
"
titl
e"
label=
"Title"
value=
{
state
.
nam
e
}
value=
{
state
.
titl
e
}
onChange=
{
inputChangeHandler
}
name=
"
nam
e"
name=
"
titl
e"
/>
</
Grid
>
...
...
@@ -81,6 +95,10 @@ const ProductForm = ({ onSubmit }: IProps) => {
/>
</
Grid
>
<
Grid
item
xs
>
<
FileInput
onChange=
{
fileChangeHandler
}
name=
"image"
label=
"Image"
/>
</
Grid
>
<
Grid
item
xs
>
<
Button
type=
"submit"
color=
"primary"
variant=
"contained"
>
Create
...
...
src/components/ProductItem/ProductItem.tsx
View file @
c6a0358f
...
...
@@ -7,17 +7,32 @@ import {
CardHeader
,
Grid
,
IconButton
,
CardMedia
,
}
from
"@mui/material"
;
import
imageNotAvailable
from
"../../assets/images/Image_not_available.png"
;
import
{
apiURL
}
from
"../../constants.ts"
;
interface
IProps
{
title
:
string
;
price
:
number
;
id
:
string
;
image
?:
string
;
}
const
ProductItem
=
({
title
,
id
,
price
}:
IProps
)
=>
{
const
ProductItem
=
({
title
,
id
,
price
,
image
}:
IProps
)
=>
{
let
cardImage
=
imageNotAvailable
;
if
(
image
)
{
cardImage
=
apiURL
+
"/uploads/"
+
image
;
}
return
(
<
Grid
item
xs=
{
12
}
sm=
{
12
}
md=
{
6
}
lg=
{
4
}
>
<
Card
>
<
Grid
item
xs=
{
12
}
sm=
{
6
}
md=
{
6
}
lg=
{
4
}
>
<
Card
sx=
{
{
minWidth
:
275
}
}
>
<
CardHeader
title=
{
title
}
/>
<
CardMedia
image=
{
cardImage
}
title=
{
title
}
sx=
{
{
height
:
200
,
paddingTop
:
"56.25%"
}
}
/>
<
CardContent
>
<
strong
style=
{
{
marginLeft
:
10
}
}
>
Price:
{
price
}
KZT
</
strong
>
</
CardContent
>
...
...
src/components/UI/Form/FileInput/FileInput.tsx
0 → 100644
View file @
c6a0358f
import
{
ChangeEvent
,
ChangeEventHandler
,
useRef
,
useState
}
from
"react"
;
import
{
Button
,
Grid
,
TextField
,
styled
}
from
"@mui/material"
;
interface
IProps
{
onChange
:
ChangeEventHandler
<
HTMLInputElement
>
;
name
:
string
;
label
:
string
;
}
const
HiddenInputFile
=
styled
(
"input"
)({
display
:
"none"
,
});
const
FileInput
=
({
onChange
,
name
,
label
}:
IProps
)
=>
{
const
inputRef
=
useRef
<
HTMLInputElement
>
(
null
);
const
[
fileName
,
setFileName
]
=
useState
(
""
);
const
onFileChange
=
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
if
(
e
.
target
.
files
&&
e
.
target
.
files
[
0
])
{
setFileName
(
e
.
target
.
files
[
0
].
name
);
}
else
{
setFileName
(
""
);
}
onChange
(
e
);
};
const
activeInput
=
()
=>
{
if
(
inputRef
.
current
)
{
inputRef
.
current
.
click
();
}
};
return
(
<>
<
HiddenInputFile
type=
"file"
name=
{
name
}
onChange=
{
onFileChange
}
ref=
{
inputRef
}
/>
<
Grid
container
direction=
"row"
spacing=
{
2
}
alignItems=
"center"
>
<
Grid
item
xs
>
<
TextField
variant=
"outlined"
disabled
fullWidth
label=
{
label
}
value=
{
fileName
}
onClick=
{
activeInput
}
/>
</
Grid
>
<
Grid
item
>
<
Button
variant=
"contained"
onClick=
{
activeInput
}
>
Browse
</
Button
>
</
Grid
>
</
Grid
>
</>
);
};
export
default
FileInput
;
src/constants.ts
0 → 100644
View file @
c6a0358f
export
const
apiURL
=
"http://localhost:8000"
;
src/containers/Products/NewProduct.tsx
View file @
c6a0358f
...
...
@@ -2,14 +2,13 @@ import { Typography } from "@mui/material";
import
ProductForm
from
"../../components/ProductForm/ProductForm.tsx"
;
import
{
useAppDispatch
}
from
"../../store/hooks.ts"
;
import
{
useNavigate
}
from
"react-router-dom"
;
import
{
CreateProductType
}
from
"../../interfaces/IProduct.ts"
;
import
{
createProduct
}
from
"../../features/productsSlice.ts"
;
const
NewProduct
=
()
=>
{
const
dispatch
=
useAppDispatch
();
const
navigate
=
useNavigate
();
const
onProductFormSubmit
=
async
(
productData
:
CreateProductType
)
=>
{
const
onProductFormSubmit
=
async
(
productData
:
FormData
)
=>
{
await
dispatch
(
createProduct
(
productData
));
navigate
(
"/"
);
};
...
...
src/containers/Products/Products.tsx
View file @
c6a0358f
...
...
@@ -31,14 +31,17 @@ const Products = () => {
</
Button
>
</
Grid
>
</
Grid
>
{
products
.
map
((
product
)
=>
(
<
ProductItem
key=
{
product
.
id
}
title=
{
product
.
name
}
price=
{
product
.
price
}
id=
{
product
.
id
}
/>
))
}
<
Grid
container
item
spacing=
{
2
}
>
{
products
.
map
((
product
)
=>
(
<
ProductItem
key=
{
product
.
id
}
title=
{
product
.
title
}
price=
{
product
.
price
}
id=
{
product
.
id
}
image=
{
product
.
image
}
/>
))
}
</
Grid
>
</
Grid
>
);
};
...
...
src/features/productsSlice.ts
View file @
c6a0358f
import
{
createAsyncThunk
,
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
CreateProductType
,
IProduct
}
from
"../interfaces/IProduct.ts"
;
import
{
IProduct
}
from
"../interfaces/IProduct.ts"
;
import
axiosApi
from
"../axiosApi.ts"
;
interface
IState
{
...
...
@@ -19,7 +19,7 @@ export const fetchProducts = createAsyncThunk("fetch/products", async () => {
export
const
createProduct
=
createAsyncThunk
(
"create/products"
,
async
(
payload
:
CreateProductType
)
=>
{
async
(
payload
:
FormData
)
=>
{
return
await
axiosApi
.
post
(
"/products"
,
payload
).
then
((
res
)
=>
res
.
data
);
},
);
...
...
src/interfaces/IProduct.ts
View file @
c6a0358f
export
interface
IProduct
{
id
:
string
;
nam
e
:
string
;
titl
e
:
string
;
description
:
string
;
price
:
number
;
image
?:
string
;
}
export
type
CreateProductType
=
Omit
<
IProduct
,
"id"
>
;
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