Commit ff9d2415 authored by Pavel Mishakov's avatar Pavel Mishakov

front lesson 84 done

parents
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "classwork",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@reduxjs/toolkit": "^1.9.3",
"axios": "^1.3.4",
"bootstrap": "^5.2.3",
"react": "^18.2.0",
"react-bootstrap": "^2.7.2",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-router-dom": "^6.8.2"
},
"devDependencies": {
"@types/axios": "^0.14.0",
"@types/bootstrap": "^5.2.6",
"@types/react": "^18.0.27",
"@types/react-bootstrap": "^0.32.32",
"@types/react-dom": "^18.0.10",
"@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "^4.9.3",
"vite": "^4.1.0"
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
import { AppDispatch, AppState } from "./store/store"
import { useDispatch, useSelector, shallowEqual } from "react-redux"
import { useEffect, useState } from "react"
import { createProduct, getProductById, getProducts } from "./store/products/products.slice"
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Col, Container, Row } from "react-bootstrap";
const App = () => {
const dispatch: AppDispatch = useDispatch()
const {products, product, messageProducts} = useSelector(
(state: AppState) => state.products, shallowEqual)
useEffect(() => {
// dispatch(getProductById('ea4f2bd1-2d5a-42ea-99e6-50a125bfe82d'))
dispatch(getProducts())
// dispatch(createProduct(
// {
// title: 'Orange',
// price: 999,
// description: 'This is an orange'
// }
// ))
}, [])
return (
<>
<h1>{messageProducts}</h1>
{JSON.stringify(products)}
<h1>Product by id: </h1>
{JSON.stringify(product)}
</>
)
}
export default App
import axios from "axios";
export const instance = axios.create({
baseURL: import.meta.env.VITE_BASE_URL
})
\ No newline at end of file
import { EStatuses } from "../enums/EStatuses"
import IProductDto from "../interfaces/IProductDto"
import IResponse from "../interfaces/IResponse"
import { instance } from "./instance"
class ProductApi {
public getProducts = async(): Promise<IResponse> => {
try {
const response = await instance.get('/products')
return response.data
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
public getProductById = async(id: string): Promise<IResponse> => {
try {
const response = await instance.get(`/products/${id}`)
return response.data
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
public createProduct = async(product: IProductDto): Promise<IResponse> => {
try {
const response = await instance.post(`/products`, product)
return response.data
} catch (err: unknown) {
const error = err as Error
const response: IResponse = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
}
export const productApi = new ProductApi()
\ No newline at end of file
export const enum EStatuses {
OK = 1,
NOT_OK = 0
}
\ No newline at end of file
body {
margin: 0;
padding: 0;
}
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
}
\ No newline at end of file
export default interface IAction {
id: string
product_id: string
supplier_id: string
action_date: Date
price: number
qty: number
}
\ No newline at end of file
export default interface IActionDto {
product_id: string
supplier_id: string
action_date: Date
price: number
qty: number
}
\ No newline at end of file
export default interface IBrand {
id: string
brand: string
}
\ No newline at end of file
export default interface IBrandDto {
brand: string
}
\ No newline at end of file
export default interface ICategory {
id: string
category: string
description: string
}
\ No newline at end of file
export default interface ICategoryDto {
category: string
description: string
}
\ No newline at end of file
import ISupplier from "./ISupplier"
export default interface IProduct {
id: string
product: string
price: number
description: string
suppliers?: ISupplier[]
}
\ No newline at end of file
export default interface IProductDto {
product: string
price: number
description: string
}
\ No newline at end of file
import { EStatuses } from "../enums/EStatuses";
import IProduct from "./IProduct";
export default interface IResponse {
result: IProduct[] | IProduct | undefined | null
message: string
status: EStatuses
}
\ No newline at end of file
import IProduct from "./IProduct"
export default interface ISupplier {
id: string
supplier: string
address: string
contacts: string
products: IProduct[]
}
\ No newline at end of file
export default interface ISupplierDto {
supplier: string
address: string
contacts: string
}
\ No newline at end of file
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux/es/exports'
import { BrowserRouter } from 'react-router-dom'
import App from './App'
import './index.css'
import store from './store/store'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
)
import { createAsyncThunk } from "@reduxjs/toolkit"
import { AppDispatch, AppState } from "./store"
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: AppState
dispatch: AppDispatch
rejectValue: string
}>()
\ No newline at end of file
import IProduct from "../../interfaces/IProduct"
export default interface IProductsState {
products: IProduct[]
product: IProduct
loadingProducts: boolean
messageProducts: string
}
\ No newline at end of file
import { createSlice } from '@reduxjs/toolkit'
import { productApi } from '../../api/productApi'
import IProduct from '../../interfaces/IProduct'
import IProductDto from '../../interfaces/IProductDto'
import { createAppAsyncThunk } from '../createAppAsyncThunk'
import IProductsState from './IProductsState'
const namespace = 'products'
export const getProducts = createAppAsyncThunk(
`${namespace}/getProducts`,
async () => {
return productApi.getProducts()
}
)
export const getProductById = createAppAsyncThunk(
`${namespace}/getProductById`,
async (id: string) => {
return productApi.getProductById(id)
}
)
export const createProduct = createAppAsyncThunk(
`${namespace}/createProduct`,
async (product: IProductDto) => {
return productApi.createProduct(product)
}
)
export const productsSlice = createSlice({
name: namespace,
initialState: {
products: [] as IProduct[],
product: {},
loadingProducts: false,
messageProducts: ''
} as IProductsState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getProducts.rejected, (state) => {
state.loadingProducts = false
})
.addCase(getProducts.pending, (state) => {
state.loadingProducts = true
})
.addCase(getProducts.fulfilled, (state, action) => {
state.loadingProducts = false
state.products = action.payload.result as IProduct[]
state.messageProducts = action.payload.message
})
.addCase(getProductById.rejected, (state) => {
state.loadingProducts = false
})
.addCase(getProductById.pending, (state) => {
state.loadingProducts = true
})
.addCase(getProductById.fulfilled, (state, action) => {
state.loadingProducts = false
state.product = action.payload.result as IProduct
state.messageProducts = action.payload.message
})
.addCase(createProduct.rejected, (state) => {
state.loadingProducts = false
})
.addCase(createProduct.pending, (state) => {
state.loadingProducts = true
})
.addCase(createProduct.fulfilled, (state, action) => {
state.loadingProducts = false
state.messageProducts = action.payload.message
})
}
})
\ No newline at end of file
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"
import { useDispatch } from 'react-redux'
import { productsSlice } from "./products/products.slice"
const makeStore = () => {
return configureStore({
reducer: {
products: productsSlice.reducer
}
})
}
const store = makeStore()
export type AppDispatch = typeof store.dispatch;
export type AppStore = ReturnType<typeof makeStore>;
export type AppState = ReturnType<AppStore["getState"]>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
AppState,
unknown,
Action
>;
export const useAppDispatch: () => AppDispatch = useDispatch;
export default store
\ No newline at end of file
/// <reference types="vite/client" />
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
port: 3000
}
})
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