This commit is contained in:
Inohara 2023-04-25 11:23:28 +04:00
parent 07bd46c0ac
commit 2babc111b9
43 changed files with 263 additions and 11071 deletions

23
1/.gitignore vendored
View File

@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@ -1,70 +0,0 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

View File

@ -1,19 +0,0 @@
{
"posts": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}

10117
1/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
{
"name": "front",
"version": "1.0.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4",
"axios": "^1.1.3",
"bootstrap": "^5.2.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/js/bootstrap.bundle.min.js" integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N" crossorigin="anonymous"></script>
<title>Front for spring</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,40 +0,0 @@
import { useRoutes, Outlet, BrowserRouter } from 'react-router-dom';
import Header from './components/common/Header';
import Catalogs from './components/catalogs/Catalogs';
import CatalogProducts from './components/catalogs/CatalogProducts';
import CatalogSuppliers from './components/catalogs/CatalogSuppliers';
function Router(props) {
return useRoutes(props.rootRoute);
}
export default function App() {
const routes = [
{ index: true, element: <Catalogs /> },
{ path: 'catalogs', element: <Catalogs />, label: 'Справочники' },
{ path: 'catalogs/products', element: <CatalogProducts/> },
{ path: 'catalogs/suppliers', element: <CatalogSuppliers /> }
];
const links = routes.filter(route => route.hasOwnProperty('label'));
const rootRoute = [
{ path: '/', element: render(links), children: routes }
];
function render(links) {
return (
<>
<Header links={links} />
<div className="container-fluid">
<Outlet />
</div>
</>
);
}
return (
<BrowserRouter>
<Router rootRoute={ rootRoute } />
</BrowserRouter>
);
}

View File

@ -1,42 +0,0 @@
import axios from "axios";
function toJSON(data) {
const jsonObj = {};
const fields = Object.getOwnPropertyNames(data);
for (const field of fields) {
if (data[field] === undefined) {
continue;
}
jsonObj[field] = data[field];
}
return jsonObj;
}
export default class DataService {
static dataUrlPrefix = 'http://localhost:8080/';
static async readAll(url, transformer) {
const response = await axios.get(this.dataUrlPrefix + url);
return response.data.map(item => transformer(item));
}
static async read(url, transformer) {
const response = await axios.get(this.dataUrlPrefix + url);
return transformer(response.data);
}
static async create(url, data) {
const response = await axios.post(this.dataUrlPrefix + url, toJSON(data));
return true;
}
static async update(url, data) {
const response = await axios.put(this.dataUrlPrefix + url, toJSON(data));
return true;
}
static async delete(url) {
const response = await axios.delete(this.dataUrlPrefix + url);
return response.data.id;
}
}

View File

@ -1,92 +0,0 @@
import React, {useState} from 'react';
const productModel ={
name: '',
cost: ''
}
export default function ProductPage(){
const mainUrl = 'http://localhost:8080/product/'
const [product, setProduct] = useState(productModel)
const [products, setProducts] = useState([])
const addProduct = (e) => {
e.preventDefault()
setProducts((prevState) => [...prevState, product])
setProduct(productModel)
}
const getAllProducts = () =>{
fetch(`${mainUrl}`)
.then((res) => {
return res.json();
})
.then((arr) => {
setProducts(arr);
})
}
return(
<div className="container">
<form id="form" onSubmit={addProduct}>
<div className="row mt-3">
<div className="col-sm-6">
<label for="lastName" className="form-label">Наименование</label>
<input type="text" className="form-control" id="productName" required
onChange={(e) => setProduct((prevState => (
{
...prevState, name: e.target.value
}
)))} value={product.name} />
</div>
<div className="col-sm-6">
<label for="firstName" className="form-label">Цена</label>
<input type="text" className="form-control" id="productCost" required
onChange={(e) => setProduct((prevState => (
{
...prevState, cost: e.target.value
}
)))} value={product.cost}/>
</div>
</div>
<div className="row mt-3">
<div className="d-grid col-sm-4 mx-auto">
<button type="submit" className="btn btn-success">Добавить</button>
</div>
<div className="d-grid col-sm-4 mx-auto mt-3 mt-sm-0">
<button type="reset" className="btn btn-danger">Очистить инпуты</button>
</div>
<div className="d-grid col-sm-4 mx-auto mt-3 mt-sm-0">
<button id="testNormal" type="button" className="btn btn-secondary">Test</button>
</div>
</div>
</form>
<div className="row table-responsive">
<table className="table mt-3">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Наименование</th>
<th scope="col">Цена</th>
<th scope="col">Действие</th>
</tr>
</thead>
<tbody id="tbody">
{products.map((product, index) =>
<tr>
<td>{index + 1}</td>
<td>{product.name}</td>
<td>{product.cost}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
)
}

View File

@ -1,113 +0,0 @@
import { useState, useEffect } from "react";
import Toolbar from "../common/Toolbar";
import Table from "../common/Table";
import Modal from "../common/Modal";
import DataService from '../DataService';
export default function Catalog(props) {
const [items, setItems] = useState([]);
const [modalHeader, setModalHeader] = useState('');
const [modalConfirm, setModalConfirm] = useState('');
const [modalVisible, setModalVisible] = useState(false);
const [isEdit, setEdit] = useState(false);
let selectedItems = [];
useEffect(() => {
loadItems();
}, []);
function loadItems() {
DataService.readAll(props.getAllUrl, props.transformer)
.then(data => setItems(data));
}
function saveItem() {
if (!isEdit) {
DataService.create(props.url, props.data).then(() => loadItems());
} else {
DataService.update(props.url + props.data.id, props.data).then(() => loadItems());
}
}
function handleAdd() {
setEdit(false);
setModalHeader('Добавление элемента');
setModalConfirm('Добавить');
setModalVisible(true);
props.onAdd();
}
function handleEdit() {
if (selectedItems.length === 0) {
return;
}
edit(selectedItems[0]);
}
function edit(editedId) {
DataService.read(props.url + editedId, props.transformer)
.then(data => {
setEdit(true);
setModalHeader('Редактирование элемента');
setModalConfirm('Сохранить');
setModalVisible(true);
props.onEdit(data);
});
}
function handleRemove() {
if (selectedItems.length === 0) {
return;
}
if (confirm('Удалить выбранные элементы?')) {
const promises = [];
selectedItems.forEach(item => {
promises.push(DataService.delete(props.url + item));
});
Promise.all(promises).then((results) => {
selectedItems.length = 0;
loadItems();
});
}
}
function handleTableClick(tableSelectedItems) {
selectedItems = tableSelectedItems;
}
function handleTableDblClick(tableSelectedItem) {
edit(tableSelectedItem);
}
function handleModalHide() {
setModalVisible(false);
}
function handleModalDone() {
saveItem();
}
return (
<>
<Toolbar
onAdd={handleAdd}
onEdit={handleEdit}
onRemove={handleRemove}/>
<Table
headers={props.headers}
items={items}
selectable={true}
onClick={handleTableClick}
onDblClick={handleTableDblClick}/>
<Modal
header={modalHeader}
confirm={modalConfirm}
visible={modalVisible}
onHide={handleModalHide}
onDone={handleModalDone}>
{props.children}
</Modal>
</>
);
}

View File

@ -1,41 +0,0 @@
import { useState } from 'react';
import Catalog from './Catalog';
import Discipline from '../../models/Discipline';
export default function CatalogOrders(props) {
const url = 'product/';
const transformer = (data) => new Discipline(data);
const catalogProductHeaders = [
{ name: 'name', label: 'Продукт' }
];
const [data, setData] = useState(new Discipline());
function handleOnAdd() {
setData(new Discipline());
}
function handleOnEdit(data) {
setData(new Discipline(data));
}
function handleFormChange(event) {
setData({ ...data, [event.target.id]: event.target.value })
}
return (
<Catalog headers={catalogProductHeaders}
getAllUrl={url}
url={url}
transformer={transformer}
data={data}
onAdd={handleOnAdd}
onEdit={handleOnEdit}>
<div className="mb-3">
<label htmlFor="name" className="form-label">Название продукта</label>
<input type="text" id="name" className="form-control" required
value={data.name} onChange={handleFormChange}/>
</div>
</Catalog>
);
}

View File

@ -1,50 +0,0 @@
import { useState } from 'react';
import Catalog from './Catalog';
import Product from '../models/Product';
export default function CatalogProducts(props) {
const url = 'product/';
const transformer = (data) => new Product(data);
const catalogProductHeaders = [
{ name: 'name', label: 'Продукт' },
{ name: 'cost', label: 'Цена' }
];
const [data, setData] = useState(new Product());
function handleOnAdd() {
setData(new Product());
}
function handleOnEdit(data) {
setData(new Product(data));
}
function handleFormChange(event) {
setData({ ...data, [event.target.id]: event.target.value })
}
return (
<Catalog headers={catalogProductHeaders}
getAllUrl={url}
url={url}
transformer={transformer}
data={data}
onAdd={handleOnAdd}
onEdit={handleOnEdit}>
<div className="mb-3">
<label htmlFor="name" className="form-label">Наименование</label>
<input type="text" id="name" className="form-control" required
value={data.name} onChange={handleFormChange}/>
</div>
<div className="mb-3">
<label htmlFor="cost" className="form-label">Цена</label>
<input type="number" id="cost" className="form-control" required
value={data.cost} onChange={handleFormChange}/>
</div>
</Catalog>
);
}

View File

@ -1,48 +0,0 @@
import { useState } from 'react';
import Catalog from './Catalog';
import Supplier from '../models/Supplier';
export default function CatalogSuppliers(props) {
const url = 'supplier/';
const transformer = (data) => new Supplier(data);
const catalogSupplierHeaders = [
{ name: 'name', label: 'Название организации' },
{ name: 'license', label: 'Номер лицензии' }
];
const [data, setData] = useState(new Supplier());
function handleOnAdd() {
setData(new Supplier());
}
function handleOnEdit(data) {
setData(new Supplier(data));
}
function handleFormChange(event) {
setData({ ...data, [event.target.id]: event.target.value })
}
return (
<Catalog headers={catalogSupplierHeaders}
getAllUrl={url}
url={url}
transformer={transformer}
data={data}
onAdd={handleOnAdd}
onEdit={handleOnEdit}>
<div className="mb-3">
<label htmlFor="name" className="form-label">Название организаци</label>
<input type="text" id="name" className="form-control" required
value={data.name} onChange={handleFormChange}/>
</div>
<div className="mb-3">
<label htmlFor="license" className="form-label">Номер лицензии</label>
<input type="text" id="license" className="form-control" required
value={data.license} onChange={handleFormChange}/>
</div>
</Catalog>
);
}

View File

@ -1,13 +0,0 @@
import LinksList from '../common/LinksList';
export default function Catalogs(props) {
const catalogs = [
{ name: 'products', label: 'Продукты' },
{ name: 'suppliers', label: 'Поставщики' },
// { name: 'orders', label: 'Заказы' }
];
return (
<LinksList items={catalogs} />
);
}

View File

@ -1,30 +0,0 @@
import { NavLink } from 'react-router-dom';
export default function Header(props) {
return (
<nav className="navbar navbar-expand-lg bg-light">
<div className="container-fluid">
<h1 className="navbar-brand">Поставки</h1>
<button className="navbar-toggler" type="button"
data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
{
props.links.map(route =>
<li key={route.path}
className="nav-item">
<NavLink className="nav-link" to={route.path}>
{route.label}
</NavLink>
</li>
)
}
</ul>
</div>
</div>
</nav >
);
}

View File

@ -1,16 +0,0 @@
import { Link } from 'react-router-dom';
export default function LinksList(props) {
return (
<div className="list-group">
{
props.items.map(catalog =>
<Link key={catalog.name} to={catalog.name}
className="list-group-item list-group-item-action">
{catalog.label}
</Link>
)
}
</div >
);
}

View File

@ -1,46 +0,0 @@
import React from "react";
export default function Modal(props) {
const formRef = React.createRef();
function hide() {
props.onHide();
}
function done(e) {
e.preventDefault();
if (formRef.current.checkValidity()) {
props.onDone();
hide();
} else {
formRef.current.reportValidity();
}
}
return (
<div className="modal fade show" tabIndex="-1" aria-hidden="true"
style={{ display: props.visible ? 'block' : 'none' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="modal-title fs-5" id="exampleModalLabel">{props.header}</h1>
<button className="btn-close" type="button" aria-label="Close"
onClick={hide}></button>
</div>
<div className="modal-body">
<form ref={formRef} onSubmit={done}>
{props.children}
</form>
</div>
<div className="modal-footer">
<button className="btn btn-secondary" type="button" onClick={hide}>Закрыть</button>
<button className="btn btn-primary" type="button" onClick={done}>
{props.confirm}
</button>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,73 +0,0 @@
import { useState } from 'react';
import styles from './Table.module.css';
export default function Table(props) {
const [tableUpdate, setTableUpdate] = useState(false);
const [selectedItems, setSelectedItems] = useState([]);
function isSelected(id) {
if (!props.selectable) {
return false;
}
return selectedItems.includes(id);
}
function click(id) {
if (!props.selectable) {
return;
}
if (isSelected(id)) {
var index = selectedItems.indexOf(id);
if (index !== -1) {
selectedItems.splice(index, 1);
setSelectedItems(selectedItems);
setTableUpdate(!tableUpdate);
}
} else {
selectedItems.push(id);
setSelectedItems(selectedItems);
setTableUpdate(!tableUpdate);
}
props.onClick(selectedItems);
}
function dblClick(id) {
if (!props.selectable) {
return;
}
props.onDblClick(id);
}
return (
<table className={`table table-hover ${styles.table} ${props.selectable ? styles.selectable : ''}`}>
<thead>
<tr>
<th scope="col">#</th>
{
props.headers.map(header =>
<th key={header.name} scope="col">
{header.label}
</th>
)
}
</tr>
</thead>
<tbody>
{
props.items.map((item, index) =>
<tr key={item.id}
className={isSelected(item.id) ? styles.selected : ''}
onClick={(e) => click(item.id, e)} onDoubleClick={(e) => dblClick(item.id, e)}>
<th scope="row">{index + 1}</th>
{
props.headers.map(header =>
<td key={item.id + header.name}>{item[header.name]}</td>
)
}
</tr>
)
}
</tbody >
</table >
);
}

View File

@ -1,12 +0,0 @@
.table tbody tr {
user-select: none;
}
.selectable tbody tr:hover {
cursor: pointer;
}
.selected {
background-color: #0d6efd;
opacity: 80%;
}

View File

@ -1,29 +0,0 @@
import styles from './Toolbar.module.css';
export default function Toolbar(props) {
function add() {
props.onAdd();
}
function edit() {
props.onEdit();
}
function remove() {
props.onRemove();
}
return (
<div className="btn-group mt-2" role="group">
<button type="button" className={`btn btn-success ${styles.btn}`} onClick={add}>
Добавить
</button>
<button type="button" className={`btn btn-warning ${styles.btn}`} onClick={edit} >
Изменить
</button >
<button type="button" className={`btn btn-danger ${styles.btn}`} onClick={remove}>
Удалить
</button >
</div >
);
}

View File

@ -1,3 +0,0 @@
.btn {
min-width: 140px;
}

View File

@ -1,7 +0,0 @@
export default class Order {
constructor(data) {
this.id = data?.id;
this.date = data?.date || '';
this.supplierId = data?.supplierId || '';
}
}

View File

@ -1,7 +0,0 @@
export default class Product {
constructor(data) {
this.id = data?.id;
this.name = data?.name || '';
this.cost = data?.cost || '';
}
}

View File

@ -1,7 +0,0 @@
export default class Supplier {
constructor(data) {
this.id = data?.id;
this.name = data?.name || '';
this.license = data?.license || '';
}
}

View File

@ -1,12 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import ProductPage from './components/ProductPage';
import './style.css'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<ProductPage />
</React.StrictMode>
);

View File

@ -35,12 +35,11 @@ public class OrderController {
return new OrderDto(orderService.addProduct(id, productId));
}
// @PatchMapping("/{id}")
// public Orders updateOrder(@PathVariable Long id,
// @RequestParam() String name,
// @RequestParam() int license) {
// return orderService.updateOrder(id, name, license);
// }
@PatchMapping("/{id}")
public Orders updateOrder(@PathVariable Long id,
@RequestParam() Long supplierId) {
return orderService.updateOrder(id, supplierId);
}
@DeleteMapping("/{id}")
public OrderDto deleteOrder(@PathVariable Long id) {

View File

@ -3,6 +3,7 @@ package com.example.demo.supply.Order;
import com.example.demo.supply.Product.Product;
import com.example.demo.supply.Supplier.Supplier;
import java.sql.Date;
import java.util.List;

View File

@ -69,9 +69,9 @@ public class OrderService {
}
@Transactional
public Orders updateOrder(Long id, Date date) {
public Orders updateOrder(Long id, Long supplierId) {
final Orders currentOrder = findOrder(id);
currentOrder.setDateOfOrder(date);
currentOrder.setSupplier(supplierService.findSupplier(supplierId));
return orderRepository.save(currentOrder);
}

View File

@ -5,6 +5,7 @@ import com.example.demo.supply.Supplier.Supplier;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

View File

@ -11,7 +11,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.3.5",
"axios": "^1.3.6",
"bootstrap": "^5.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -5078,9 +5078,9 @@
}
},
"node_modules/axios": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
"integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",

View File

@ -5,7 +5,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.3.5",
"axios": "^1.3.6",
"bootstrap": "^5.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-aFq/bzH65dt+w6FI2ooMVUpc+21e0SRygnTpmBvdBgSdnuTN7QbdgL+OapgHtvPp" crossorigin="anonymous">
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha2/dist/js/bootstrap.bundle.min.js" integrity="sha384-qKXV1j0HvMUeCBQ+QVp7JcfGl760yU08IQ+GpUo5hlbpg51QRiuqHAJz8+BrxE/N" crossorigin="anonymous"></script>
<link rel="stylesheet" href="path/to/font-awesome/css/font-awesome.min.css">
<title>React App</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe" crossorigin="anonymous"></script>
<title>Поставки</title>
</head>
<body>
<div id="root"></div>

View File

@ -1,6 +1,7 @@
import { BrowserRouter, Route, Routes} from "react-router-dom";
import CatalogProducts from "./Catalogs/CatalogProducts";
import CatalogSuppliers from "./Catalogs/CatalogSuppliers";
import CatalogOrders from "./Catalogs/CatalogOrders";
import Header from "./general/Header";
function App() {
@ -12,7 +13,7 @@ function App() {
<Route path="/" Component={CatalogProducts}/>
<Route path="/products" Component={CatalogProducts} />
<Route path="/suppliers" Component={CatalogSuppliers} />
<Route path="/orders" Component={CatalogSuppliers} />
<Route path="/orders" Component={CatalogOrders} />
</Routes>
</BrowserRouter>

View File

@ -1,9 +1,15 @@
import { useState } from 'react';
import { React, useState, useEffect } from 'react';
import Catalog from '../general/Catalog'
import Order from '../models/Order';
import Supplier from '../models/Supplier';
import Product from '../models/Product';
import DataService from '../DataService';
import Modal from '../general/Modal';
export default function CatalogOrders(props) {
const url = 'order/'
const supplierUrl = 'supplier/'
const productUrl = 'product/'
const transformer = (data) => new Order(data)
@ -14,45 +20,88 @@ export default function CatalogOrders(props) {
];
const [data, setData] = useState(new Order());
const [suppliers, setSuppliers] = useState([]);
const [products, setProducts] = useState([]);
const add = () => setData(new Order());
const edit = (data) => setData(new Order(data))
useEffect(() => {
DataService.readAll(supplierUrl, (data) => new Supplier(data)).then(data => setSuppliers(data));
DataService.readAll(productUrl, (data) => new Product(data)).then(data => setProducts(data));
}, [])
const add = () =>{
setData(new Order())
console.log(data)
}
const edit = (data) => {
console.log(data)
setData(new Order(data))
}
const addProduct = (data) => {
setData(new Order(data))
}
function handleFormChange(event) {
console.log(data)
setData({ ...data, [event.target.id]: event.target.value })
console.log(data)
}
return (
<Catalog headers={catalogOrderHeaders}
getAllUrl={url}
<>
<Catalog headers={catalogOrderHeaders}
url={url}
transformer={transformer}
data={data}
add={add}
edit={edit}>
<div className="mb-3">
<label htmlFor="data" className="form-label" value={`Дата ${data.date}`}>Дата ${data.date}</label>
edit={edit}
isOrder={true}
addProduct={edit}
modalAddProduct={
<div className="mb-3">
<label htmlFor="product" className="form-label">Поставщик</label>
<select id="product" className="form-select" required
value={data.product} onChange={handleFormChange}>
<option disabled value="">Укажите продукт</option>
{
products.map(product =>
<option key={product.id} value={product.id}>{product.name}</option>
)
}
</select>
</div>
}>
{/* <div className="mb-3">
<label htmlFor="data" className="form-label">Дата {data.date}</label>
</div> */}
<div className="mb-3">
<label htmlFor="cost" className="form-label">Цена</label>
<input type="number" id="cost" className="form-control" required
value={data.cost} onChange={handleFormChange}/>
</div>
<div className="mb-3">
<label htmlFor="supplier" className="form-label">Поставщик</label>
<select id="supplier" className="form-select" required
value={data.supplier} onChange={handleFormChange}>
<label htmlFor="supplierId" className="form-label">Поставщик</label>
<select id="supplierId" className="form-select" required
value={data.supplierId} onChange={handleFormChange}>
<option disabled value="">Укажите поставщика</option>
{
groups.map(group =>
<option key={group.id} value={group.id}>{group.name}</option>
suppliers.map(supplier =>
<option key={supplier.id} value={supplier.id}>{supplier.name}</option>
)
}
</select>
</div>
<div className="mb-3">
<label htmlFor="product" className="form-label">Поставщик</label>
<select id="product" className="form-select" required
value={data.product} onChange={handleFormChange}>
<option disabled value="">Укажите продукт</option>
{
products.map(product =>
<option key={product.id} value={product.id}>{product.name}</option>
)
}
</select>
</div>
</Catalog>
</>
);
}

View File

@ -29,7 +29,8 @@ export default function CatalogProducts(props) {
transformer={transformer}
data={data}
add={add}
edit={edit}>
edit={edit}
isOrder={false}>
<div className="mb-3">
<label htmlFor="name" className="form-label">Наименование</label>
<input type="text" id="name" className="form-control" required

View File

@ -28,7 +28,8 @@ export default function CatalogSuppliers(props) {
transformer={transformer}
data={data}
add={add}
edit={edit}>
edit={edit}
isOrder={false}>
<div className="mb-3">
<label htmlFor="name" className="form-label">Поставщик</label>
<input type="text" id="name" className="form-control" required

View File

@ -6,9 +6,12 @@ function getFullUrl(url, data) {
const fields = Object.getOwnPropertyNames(data);
//проходимся по каждому полю
for (const field of fields) {
if (data[field] === undefined) {
continue;
}
if (field === undefined) continue
if (field === 'id') continue
if (field === 'date') continue
if (field === 'countProducts') continue
if (field === 'products') continue
currentUrl.searchParams.append(field, data[field])
}
@ -20,6 +23,7 @@ export default class DataService {
static async readAll(url, transformer) {
const response = await axios.get(this.mainUrl + url);
// console.log(response)
return response.data.map(item => transformer(item));
}
@ -29,14 +33,21 @@ export default class DataService {
}
static async create(url, data) {
console.log(data)
await fetch(getFullUrl(this.mainUrl + url, data), {
method: 'POST',
}).catch(e => console.log(e))
return true;
}
static async addProduct(url) {
await fetch(url, {
method: 'PATCH',
}).catch(e => console.log(e))
return true;
}
static async update(url, data) {
data.id = undefined
await fetch(getFullUrl(this.mainUrl + url, data), {
method: 'PATCH',
}).catch(e => console.log(e))

View File

@ -0,0 +1,54 @@
import React from "react";
export default function AddsToOrder(props){
const formRef = React.createRef();
const hide = () => props.onHide()
const done = (e) => {
e.preventDefault()
if (formRef.current.checkValidity()) {
props.onDone()
hide()
} else
formRef.current.reportValidity()
}
return(
<div className="modal fade show" tabIndex="-1" aria-hidden="true"
style={{ display: props.visible ? 'block' : 'none' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="modal-title fs-5" id="exampleModalLabel">{props.header}</h1>
<button className="btn-close" type="button" aria-label="Close"
onClick={hide}></button>
</div>
<div className="modal-body">
<form ref={formRef} onSubmit={done}>
<div className="mb-3">
<label htmlFor="product" className="form-label">Поставщик</label>
<select id="product" className="form-select" required
value={data.product} onChange={handleFormChange}>
<option disabled value="">Укажите продукт</option>
{
props.products.map(product =>
<option key={product.id} value={product.id}>{product.name}</option>
)
}
</select>
</div>
</form>
</div>
<div className="modal-footer">
<button className="btn btn-secondary" type="button" onClick={hide}>Закрыть</button>
<button className="btn btn-primary" type="button" onClick={done}>
{props.confirm}
</button>
</div>
</div>
</div>
</div>
)
}

View File

@ -10,7 +10,10 @@ function Catalog(props) {
const [modalHeader, setModalHeader] = useState('')
const [modalConfirm, setModalConfirm] = useState('')
const [modalVisible, setModalVisible] = useState(false)
const [addProdVisible, setAddProdVisible] = useState(false)
const [isEdit, setEdit] = useState(false)
const [isAddProd, setIsAddProd] = useState(false)
let selectedItems = [];
@ -20,15 +23,22 @@ function Catalog(props) {
}, []);
const loadItems = () => {
DataService.readAll(props.getAllUrl, props.transformer)
.then(data => setItems(data))
DataService.readAll(props.url, props.transformer)
.then(data =>
setItems(data))
}
const saveItem = () => {
if(isAddProd){
DataService.addProduct(`${props.url}addProduct/${props.data.id}`).then(() => loadItems())
return
}
if (!isEdit) {
DataService.create(props.url, props.data).then(() => loadItems());
} else {
DataService.update(props.url + props.data.id, props.data).then(() => loadItems());
DataService.create(props.url, props.data).then(() => loadItems())
if()
} else{
DataService.update(props.url + props.data.id, props.data).then(() => loadItems())
}
}
@ -44,7 +54,16 @@ function Catalog(props) {
if (selectedItems.length === 0) {
return;
}
editItem(selectedItems[0])
//editItem(selectedItems[0])
DataService.read(props.url + selectedItems[0], props.transformer)
.then(data => {
setEdit(true);
setModalHeader('Редактирование элемента');
setModalConfirm('Сохранить');
setModalVisible(true);
props.edit(data);
});
}
const editItem = (editedId) => {
@ -75,12 +94,24 @@ function Catalog(props) {
}
}
const addProduct = () => {
if (selectedItems.length === 0)
return
DataService.read(props.url + selectedItems[0], props.transformer)
.then(data => {
setEdit(false)
isAddProd(true)
setModalHeader('Добавление продукта к заказу')
setModalConfirm('Сохранить')
setAddProdVisible(true)
props.edit(data)
});
}
const handleTableClick = (tableSelectedItems) => selectedItems = tableSelectedItems;
const handleTableDblClick = (tableSelectedItem) => editItem(tableSelectedItem);
const hideModal = () => setModalVisible(false)
//после принятия в модальном окне идет создание, либо изменение объекта
const modalDone = () => saveItem()
return (
@ -88,7 +119,10 @@ function Catalog(props) {
<ToolBar
add={add}
edit={edit}
remove={remove}/>
remove={remove}
addProduct={addProduct}
// removeProduct
addsVisible={props.isOrder}/>
<Table
headers={props.headers}
items={items}
@ -103,6 +137,14 @@ function Catalog(props) {
onDone={modalDone}>
{props.children}
</Modal>
<Modal
header={modalHeader}
confirm={modalConfirm}
visible={addProdVisible}
onHide={hideModal}
onDone={modalDone}>
{props.modalAddProduct}
</Modal>
</>
);
}

View File

@ -19,28 +19,28 @@ function Modal(props) {
return (
<div className="modal fade show" tabIndex="-1" aria-hidden="true"
style={{ display: props.visible ? 'block' : 'none' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="modal-title fs-5" id="exampleModalLabel">{props.header}</h1>
<button className="btn-close" type="button" aria-label="Close"
onClick={hide}></button>
</div>
<div className="modal-body">
<form ref={formRef} onSubmit={done}>
{props.children}
</form>
</div>
<div className="modal-footer">
<button className="btn btn-secondary" type="button" onClick={hide}>Закрыть</button>
<button className="btn btn-primary" type="button" onClick={done}>
{props.confirm}
</button>
</div>
style={{ display: props.visible ? 'block' : 'none' }}>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="modal-title fs-5" id="exampleModalLabel">{props.header}</h1>
<button className="btn-close" type="button" aria-label="Close"
onClick={hide}></button>
</div>
<div className="modal-body">
<form ref={formRef} onSubmit={done}>
{props.children}
</form>
</div>
<div className="modal-footer">
<button className="btn btn-secondary" type="button" onClick={hide}>Закрыть</button>
<button className="btn btn-primary" type="button" onClick={done}>
{props.confirm}
</button>
</div>
</div>
</div>
</div>
);
}

View File

@ -4,30 +4,34 @@ import styles from './Toolbar.module.css';
//кнопочки
function ToolBar(props) {
const add = () => {
props.add()
}
const edit = () => {
props.edit()
}
const remove = () => {
props.remove()
}
const add = () => props.add()
const edit = () => props.edit()
const remove = () => props.remove()
const addProduct = () => props.addProduct()
const removeProduct = () => props.removeProduct()
return (
<div className="btn-group mt-2" role="group">
<button type="button" className={`btn btn-success ${styles.btn}`} onClick={add}>
Добавить
</button>
<button type="button" className={`btn btn-warning ${styles.btn}`} onClick={edit} >
Изменить
</button >
<button type="button" className={`btn btn-danger ${styles.btn}`} onClick={remove}>
Удалить
</button >
</div >
<button type="button" className={`btn btn-success ${styles.btn}`} onClick={add}>
Добавить
</button>
<button type="button" className={`btn btn-warning ${styles.btn}`} onClick={edit} >
Изменить
</button >
<button type="button" className={`btn btn-danger ${styles.btn}`} onClick={remove}>
Удалить
</button >
<button type="button" className={`btn btn-success ${styles.btn}`} onClick={addProduct}
style={{ display: props.addsVisible ? 'block' : 'none' }}>
Добавить продукт
</button>
<button type="button" className={`btn btn-danger ${styles.btn}`} onClick={removeProduct}
style={{ display: props.addsVisible ? 'block' : 'none' }}>
Удалить продукт
</button >
</div >
);
}

View File

@ -1,3 +1,4 @@
.btn {
min-width: 140px;
margin: 5px;
}

View File

@ -3,13 +3,8 @@ import Product from "./Product";
export default class Order {
constructor(data) {
this.id = data?.id;
this.date = data?.date || '';
this.supplier = data?.supplier || '';
this.supplierId = data?.supplierId || '';
this.products = data?.products.map(product => new Product(product)) || '';
this.countProducts = '';
if(this.countProducts !== ''){
this.countProducts = this.products.length
}
this.date = data?.dateOfOrder || '';
this.supplierId = data?.supplier.id || '';
this.supplier = data?.supplier.name || '';
}
}