Четвёртая лабораторная работа. Frontend.

This commit is contained in:
abazov73 2023-04-25 14:55:16 +04:00
parent c6b23e8d96
commit d652956ae9
20 changed files with 708 additions and 20 deletions

Binary file not shown.

View File

@ -28,6 +28,21 @@ public class ProductController {
.toList(); .toList();
} }
//сделать запрос + пагинация
@GetMapping("/getWithStores")
public List<ProductDTO> getProductsWithStores(){
return productService.getAllProducts().stream().filter(prod -> prod.getStore() != null)
.map(ProductDTO::new)
.toList();
}
@GetMapping("/getWithoutStores")
public List<ProductDTO> getProductsWithoutStores(){
return productService.getAllProducts().stream().filter(prod -> prod.getStore() == null)
.map(ProductDTO::new)
.toList();
}
@PostMapping @PostMapping
public ProductDTO createProduct(@RequestParam("productName") String productName){ public ProductDTO createProduct(@RequestParam("productName") String productName){
final Product product = productService.addProduct(productName); final Product product = productService.addProduct(productName);

View File

@ -44,9 +44,9 @@ public class StoreController {
return new StoreDTO(storeService.updateStore(id, storeName)); return new StoreDTO(storeService.updateStore(id, storeName));
} }
@PutMapping("/{storeId}-{productId}") @PutMapping("/add")
public ProductDTO addProduct(@PathVariable Long storeId, public ProductDTO addProduct(@RequestParam("storeId") Long storeId,
@PathVariable Long productId){ @RequestParam("productId") Long productId){
return new ProductDTO(storeService.addProduct(storeId, productId)); return new ProductDTO(storeService.addProduct(storeId, productId));
} }

View File

@ -7,12 +7,14 @@ public class OrderedDTO {
public final int quantity; public final int quantity;
public final String productName; public final String productName;
public final String customerFIO; public final String customerFIO;
public final String storeName;
public OrderedDTO(Ordered ordered){ public OrderedDTO(Ordered ordered){
this.id = ordered.getId(); this.id = ordered.getId();
this.quantity = ordered.getQuantity(); this.quantity = ordered.getQuantity();
this.productName = ordered.getProduct().getName(); this.productName = ordered.getProduct().getName();
this.customerFIO = ordered.getCustomer().getLastName() + ordered.getCustomer().getFirstName() + ordered.getCustomer().getMiddleName(); this.storeName = ordered.getProduct().getStore().getStoreName();
this.customerFIO = ordered.getCustomer().getLastName() + " " + ordered.getCustomer().getFirstName() + " " + ordered.getCustomer().getMiddleName();
} }
public Long getId() { public Long getId() {
@ -30,4 +32,8 @@ public class OrderedDTO {
public String getCustomerFIO() { public String getCustomerFIO() {
return customerFIO; return customerFIO;
} }
public String getStoreName() {
return storeName;
}
} }

View File

@ -6,13 +6,13 @@ import java.util.List;
public class ProductDTO { public class ProductDTO {
public final Long id; public final Long id;
public final String name; public final String productName;
public final String storeName; public final String storeName;
public ProductDTO(Product product){ public ProductDTO(Product product){
this.id = product.getId(); this.id = product.getId();
this.name = product.getName(); this.productName = product.getName();
this.storeName = product.getStore().getStoreName(); this.storeName = product.getStore() == null ? null : product.getStore().getStoreName();
} }
public Long getId() { public Long getId() {
@ -20,7 +20,7 @@ public class ProductDTO {
} }
public String getName() { public String getName() {
return name; return productName;
} }
public String getStoreName() { public String getStoreName() {

View File

@ -38,8 +38,10 @@ public class Product {
} }
@PreRemove @PreRemove
public void removeStore(){ public void removeStore(){
this.store.getProducts().remove(this); if (this.store != null) {
this.store = null; this.store.getProducts().remove(this);
this.store = null;
}
removeOrders(); removeOrders();
} }

View File

@ -1,6 +1,5 @@
package com.example.ipLab.StoreDataBase.Model; package com.example.ipLab.StoreDataBase.Model;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import jakarta.persistence.*; import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@ -18,7 +17,9 @@ public class Store {
private String storeName; private String storeName;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true) @OneToMany(fetch = FetchType.EAGER, mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products; private List<Product> products;
public Store(){} public Store(){
this.products = new ArrayList<>();
}
public Store(String storeName){ public Store(String storeName){
this.storeName = storeName; this.storeName = storeName;
this.products = new ArrayList<>(); this.products = new ArrayList<>();
@ -47,7 +48,7 @@ public class Store {
products) { products) {
product.removeStore(); product.removeStore();
} }
products = null; products = new ArrayList<>();
} }
public void setStoreName(String storeName) { public void setStoreName(String storeName) {

View File

@ -1,7 +1,10 @@
import { useRoutes, Outlet, BrowserRouter } from 'react-router-dom'; import { useRoutes, Outlet, BrowserRouter } from 'react-router-dom';
import Header from './components/common/Header'; import Header from './components/common/Header';
import Footer from './components/common/Footer'; import CustomerPage from './components/pages/customerPage';
import CustomerPage from './components/pages/customerPage' import StorePage from './components/pages/storePage';
import ProductPage from './components/pages/productPage';
import OrderPage from './components/pages/orderPage';
import AddToStorePage from './components/pages/addToStorePage'
import './styleSite.css'; import './styleSite.css';
function Router(props) { function Router(props) {
@ -12,9 +15,10 @@ export default function App() {
const routes = [ const routes = [
{ index: true, element: <CustomerPage/> }, { index: true, element: <CustomerPage/> },
{ path: 'customer', element: <CustomerPage/>, label:'Покупатели'}, { path: 'customer', element: <CustomerPage/>, label:'Покупатели'},
// { path: 'shop', element: <Shop/>, label: 'Магазины' }, { path: 'store', element: <StorePage/>, label: 'Магазины' },
// { path: 'product', element: <Product/>, label: 'Товары'}, { path: 'product', element: <ProductPage/>, label: 'Товары' },
// { path: 'order', element: <Order/>, label: 'Заказы'} { path: 'order', element: <OrderPage/>, label: 'Заказы'},
{ path: 'addToStore', element: <AddToStorePage/>, label: 'Доставка'}
]; ];
const links = routes.filter(route => route.hasOwnProperty('label')); const links = routes.filter(route => route.hasOwnProperty('label'));
const rootRoute = [ const rootRoute = [
@ -29,7 +33,6 @@ export default function App() {
<div className="container-fluid"> <div className="container-fluid">
<Outlet /> <Outlet />
</div> </div>
<Footer/>
</> </>
); );
} }

View File

@ -10,7 +10,7 @@ export default function Header(props){
<img src="logo.png" alt="*" width="60" height="60" className="align-text-top"></img> <img src="logo.png" alt="*" width="60" height="60" className="align-text-top"></img>
</a> </a>
<div id="logoName"> <div id="logoName">
<a href="mainPage">boxStore</a> <a href="customer">boxStore</a>
</div> </div>
</div> </div>
<nav className="navbar navbar-expand-md"> <nav className="navbar navbar-expand-md">

View File

@ -0,0 +1,15 @@
import styles from './Toolbar.module.css';
export default function ToolbarOrder(props) {
function add() {
props.onAdd();
}
return (
<div className="btn-group my-2 mx-4" role="group">
<button type="button" className={`btn btn-success ${styles.btn}`} onClick={add}>
Добавить
</button>
</div >
);
}

View File

@ -0,0 +1,116 @@
import { useState, useEffect } from "react";
import Modal from './Modal';
import DataService from '../../services/DataService';
import OrderToolbar from './ToolbarOrder';
import Table from './Table';
export default function CustomerTable(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();
loadOptions();
}, []);
function loadItems() {
DataService.readAll(props.getAllUrl, props.transformer)
.then(data => setItems(data));
}
async function loadOptions(){
props.loadOptions(await DataService.readAll(props.getCustomerUrl, props.transformerCustomer), await DataService.readAll(props.getProductUrl, props.transformerProduct));
}
function saveItem() {
if (!isEdit) {
DataService.create(props.url, "?productId=" + props.data.productId + "&customerId=" + props.data.customerId + "&quantity=" + props.data.quantity).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, "?productId=" + props.data.productId + "&customerId=" + props.data.customerId + "&quantity=" + props.data.quantity).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.getUrl + 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.getUrl + 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(
<>
<OrderToolbar
onAdd={handleAdd}/>
<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

@ -0,0 +1,113 @@
import { useState, useEffect } from "react";
import Modal from './Modal';
import DataService from '../../services/DataService';
import Toolbar from './Toolbar';
import Table from './Table';
export default function CustomerTable(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, "?productName=" + props.data.productName).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, "?productName=" + props.data.productName).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.getUrl + 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.getUrl + 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

@ -0,0 +1,113 @@
import { useState, useEffect } from "react";
import Modal from './Modal';
import DataService from '../../services/DataService';
import Toolbar from './Toolbar';
import Table from './Table';
export default function CustomerTable(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, "?storeName=" + props.data.storeName).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, "?storeName=" + props.data.storeName).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.getUrl + 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.getUrl + 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

@ -0,0 +1,87 @@
import Product from "../../models/product"
import Store from "../../models/store"
import DataService from '../../services/DataService';
import { useState, useEffect} from "react";
export default function AddToStorePage(){
const getStoreUrl = 'store';
const getProductUrl = 'product/getWithoutStores'
const url = 'store/add'
const [storeOptions, setStoreOptions] = useState([])
const [productOptions, setProductOptions] = useState([])
const transformerProduct = (data) => new Product(data);
const transformerStore = (data) => new Store(data);
useEffect(() => {
loadOptions();
}, []);
async function loadOptions(){
loadSelOptions(await DataService.readAll(getStoreUrl, transformerStore), await DataService.readAll(getProductUrl, transformerProduct));
}
function loadSelOptions(dataStore, dataProduct){
const results1 = [];
//console.log(dataProduct);
dataStore.forEach((value) => {
results1.push({
key: value.storeName,
value: value.id,
})
})
console.log(results1);
setStoreOptions(results1);
const results2 = [];
console.log(dataProduct);
dataProduct.forEach((value) => {
results2.push({
key: value.productName,
value: value.id,
})
})
setProductOptions(results2);
}
function add(){
var storeId = document.getElementById("storeId").value;
var productId = document.getElementById("productId").value;
DataService.update(url, "?storeId=" + storeId + "&productId=" + productId);
window.location.replace("/product");
}
return(
<>
<div className="col-md-4">
<label className="form-label" forhtml="storeId">Магазин</label>
<select className="form-select" id="storeId" required>
{
storeOptions.map((option) => {
return(
<option key={option.value} value={option.value}>
{option.key}
</option>
)
}
)
}
</select>
<label className="form-label" forhtml="productId">Товар</label>
<select className="form-select" id="productId" required>
{
productOptions.map((option) => {
return(
<option key={option.value} value={option.value}>
{option.key}
</option>
)
}
)
}
</select>
<button className={`btn btn-success`} onClick={add}>
Добавить в магазин
</button>
</div>
</>
);
}

View File

@ -0,0 +1,108 @@
import Order from "../../models/order"
import Product from "../../models/product"
import Customer from "../../models/customer";
import OrderTable from '../common/orderTable'
import { useState, useEffect} from "react";
export default function OrderPage(){
const url = 'order';
const getUrl = 'order/';
const getCustomerUrl = 'customer';
const getProductUrl = 'product/getWithStores'
const transformer = (data) => new Order(data);
const transformerProduct = (data) => new Product(data);
const transformerCustomer = (data) => new Customer(data);
const catalogOrderHeaders = [
{ name: 'customerFIO', label: 'ФИО покупателя' },
{ name: 'storeName', label: 'Магазина'},
{ name: 'productName', label: 'Товар'}
];
const [data, setData] = useState(new Order());
const [customerOptions, setCustomerOptions] = useState([])
const [productOptions, setProductOptions] = useState([])
function loadOptions(dataCustomer, dataProduct){
const results1 = [];
console.log(dataCustomer);
//console.log(dataProduct);
dataCustomer.forEach((value) => {
results1.push({
key: value.lastName + " " + value.firstName + " " + value.middleName,
value: value.id,
})
})
console.log(results1);
setCustomerOptions(results1);
const results2 = [];
dataProduct.forEach((value) => {
results2.push({
key: value.productName,
value: value.id,
})
})
setProductOptions(results2);
//console.log(customerOptions);
//console.log(productOptions);
}
function handleOnAdd() {
setData(new Order());
}
function handleOnEdit(data) {
setData(new Order(data));
}
function handleFormChange(event) {
setData({ ...data, [event.target.id]: event.target.value })
}
return(
<article className="h-100 mt-0 mb-0 d-flex flex-column justify-content-between">
<OrderTable headers={catalogOrderHeaders}
getAllUrl={url}
url={url}
getUrl={getUrl}
getCustomerUrl={getCustomerUrl}
getProductUrl={getProductUrl}
transformer={transformer}
transformerCustomer={transformerCustomer}
transformerProduct={transformerProduct}
data={data}
onAdd={handleOnAdd}
loadOptions={loadOptions}
onEdit={handleOnEdit}>
<div className="col-md-4">
<label className="form-label" forhtml="customerId">Покупатель</label>
<select className="form-select" id="customerId" value={data.customerId} onChange={handleFormChange} required>
{
customerOptions.map((option) => {
return(
<option key={option.value} value={option.value}>
{option.key}
</option>
)
}
)
}
</select>
<label className="form-label" forhtml="productId">Продукт</label>
<select className="form-select" id="productId" value={data.customerId} onChange={handleFormChange} required>
{
productOptions.map((option) => {
return(
<option key={option.value} value={option.value}>
{option.key}
</option>
)
}
)
}
</select>
<label className="form-label" forhtml="quantity">Количество</label>
<input className="form-control" type="number" id="quantity" value={data.quantity} onChange={handleFormChange} placeholder="1" step="1" min="1" required="required"/>
</div>
</OrderTable>
</article>
)
}

View File

@ -0,0 +1,44 @@
import Product from "../../models/product"
import ProductTable from '../common/productTable'
import { useState, useEffect} from "react";
export default function ProductPage(){
const url = 'product';
const getUrl = 'product/';
const transformer = (data) => new Product(data);
const catalogProductHeaders = [
{ name: 'productName', label: 'Название товара' },
{ name: 'storeName', 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(
<article className="h-100 mt-0 mb-0 d-flex flex-column justify-content-between">
<ProductTable headers={catalogProductHeaders}
getAllUrl={url}
url={url}
getUrl={getUrl}
transformer={transformer}
data={data}
onAdd={handleOnAdd}
onEdit={handleOnEdit}>
<div className="col-md-4">
<label className="form-label" forhtml="productName">Название</label>
<input className="form-control" type="text" id="productName" value={data.lastName} onChange={handleFormChange} required="required"/>
</div>
</ProductTable>
</article>
)
}

View File

@ -0,0 +1,43 @@
import Store from "../../models/store"
import StoreTable from '../common/storeTable'
import { useState, useEffect} from "react";
export default function StorePage(){
const url = 'store';
const getUrl = 'store/';
const transformer = (data) => new Store(data);
const catalogStoreHeaders = [
{ name: 'storeName', label: 'Название магазина' }
];
const [data, setData] = useState(new Store());
function handleOnAdd() {
setData(new Store());
}
function handleOnEdit(data) {
setData(new Store(data));
}
function handleFormChange(event) {
setData({ ...data, [event.target.id]: event.target.value })
}
return(
<article className="h-100 mt-0 mb-0 d-flex flex-column justify-content-between">
<StoreTable headers={catalogStoreHeaders}
getAllUrl={url}
url={url}
getUrl={getUrl}
transformer={transformer}
data={data}
onAdd={handleOnAdd}
onEdit={handleOnEdit}>
<div className="col-md-4">
<label className="form-label" forhtml="storeName">Название</label>
<input className="form-control" type="text" id="storeName" value={data.lastName} onChange={handleFormChange} required="required"/>
</div>
</StoreTable>
</article>
)
}

View File

@ -0,0 +1,8 @@
export default class Store {
constructor(data) {
this.id = data?.id;
this.storeName = data?.storeName || '';
this.customerFIO = data?.customerFIO || '';
this.productName = data?.productName || '';
}
}

View File

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

View File

@ -0,0 +1,7 @@
export default class Store {
constructor(data) {
this.id = data?.id;
this.storeName = data?.storeName || '';
this.products = data?.products || null;
}
}