Commit c6a0358f authored by Kulpybaev Ilyas's avatar Kulpybaev Ilyas

Урок 89

parent 7d5d1079
import axios from "axios";
import { apiURL } from "./constants.ts";
const axiosApi = axios.create({
baseURL: "http://localhost:8000",
baseURL: apiURL,
});
export default axiosApi;
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 {
name: string;
title: 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>({
name: "",
title: "",
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();
Object.keys(state).forEach((key) => {
formData.append(key, state[key as keyof IState]);
});
onSubmit(newProduct);
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="name"
id="title"
label="Title"
value={state.name}
value={state.title}
onChange={inputChangeHandler}
name="name"
name="title"
/>
</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
......
......@@ -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>
......
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;
export const apiURL = "http://localhost:8000";
......@@ -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("/");
};
......
......@@ -31,15 +31,18 @@ const Products = () => {
</Button>
</Grid>
</Grid>
<Grid container item spacing={2}>
{products.map((product) => (
<ProductItem
key={product.id}
title={product.name}
title={product.title}
price={product.price}
id={product.id}
image={product.image}
/>
))}
</Grid>
</Grid>
);
};
......
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);
},
);
......
export interface IProduct {
id: string;
name: string;
title: string;
description: string;
price: number;
image?: string;
}
export type CreateProductType = Omit<IProduct, "id">;
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