Compare commits

..

13 Commits

Author SHA1 Message Date
92c4fdc228 Добавлен отчёт №5 2023-06-18 13:13:29 +04:00
abazov73
f4e33caffe Пятая лабораторная работа. Готово. 2023-05-02 17:01:30 +04:00
abazov73
20af7ec707 Пятая лабораторная работа. Фикс ссылки на главную 2023-05-02 08:40:50 +04:00
abazov73
4a27990626 Пятая лабораторная работа. Начала работы с MVC + CustomerMVCController. 2023-05-02 01:29:14 +04:00
abazov73
d29a9a5e25 Четвёртая лабораторная работа. Фикс отбора товаров по признаку привязки к магазину. 2023-04-30 21:07:58 +04:00
abazov73
d652956ae9 Четвёртая лабораторная работа. Frontend. 2023-04-25 14:55:16 +04:00
abazov73
c6b23e8d96 Четвёртая лабораторная работа. Frontend без страниц для Order, Product, Store. 2023-04-25 11:52:45 +04:00
abazov73
5568a57217 Четвёртая лабораторная работа. Backend 2023-04-25 00:35:21 +04:00
abazov73
61813642af Третья лабораторная работа. Доп. задание 2023-04-11 14:42:54 +04:00
abazov73
c6dff060b0 Третья лабораторная работа. Фикс сервиса продукта + фикс установки связей OneToMany 2023-04-11 12:51:22 +04:00
abazov73
49195ca6b4 Третья лабораторная работа. Тесты. 2023-04-10 21:49:50 +04:00
abazov73
0f2260935c Третья лабораторная работа. 2023-04-10 19:39:05 +04:00
abazov73
4a7874a0a7 Вторая лабораторная работа. 2023-03-14 09:47:00 +04:00
92 changed files with 13430 additions and 162 deletions

View File

@ -1,6 +1,6 @@
plugins { plugins {
id 'java' id 'java'
id 'org.springframework.boot' version '3.0.2' id 'org.springframework.boot' version '3.0.1'
id 'io.spring.dependency-management' version '1.1.0' id 'io.spring.dependency-management' version '1.1.0'
} }
@ -14,7 +14,22 @@ repositories {
dependencies { dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2:2.1.210'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.hibernate.validator:hibernate-validator'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation 'org.webjars:bootstrap:5.1.3'
implementation 'org.webjars:jquery:3.6.0'
implementation 'org.webjars:font-awesome:6.1.0'
} }
tasks.named('test') { tasks.named('test') {

BIN
backend/ipLab/data.mv.db Normal file

Binary file not shown.

5076
backend/ipLab/data.trace.db Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,54 @@
package com.example.ipLab.StoreDataBase.Controllers;
import com.example.ipLab.StoreDataBase.DTO.CustomerDTO;
import com.example.ipLab.StoreDataBase.Model.Customer;
import com.example.ipLab.StoreDataBase.Service.CustomerService;
import com.example.ipLab.WebConfiguration;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/customer")
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService){
this.customerService = customerService;
}
@GetMapping("/{id}")
public CustomerDTO getCustomer(@PathVariable Long id){
return new CustomerDTO(customerService.getCustomer(id));
}
@GetMapping
public List<CustomerDTO> getCustomers(){
return customerService.getAllCustomers().stream()
.map(CustomerDTO::new)
.toList();
}
@PostMapping
public CustomerDTO createCustomer(@RequestBody @Valid CustomerDTO customerDTO){
final Customer customer = customerService.addCustomer(customerDTO.getlastName(), customerDTO.getfirstName(), customerDTO.getmiddleName());
return new CustomerDTO(customer);
}
@PutMapping("/{id}")
public CustomerDTO updateCustomer(@RequestBody @Valid CustomerDTO customerDTO,
@PathVariable Long id){
return new CustomerDTO(customerService.updateCustomer(id, customerDTO.getlastName(), customerDTO.getfirstName(), customerDTO.getmiddleName()));
}
@DeleteMapping("/{id}")
public CustomerDTO deleteCustomer(@PathVariable Long id){
return new CustomerDTO(customerService.deleteCustomer(id));
}
@DeleteMapping()
public void deleteAllCustomers(){
customerService.deleteAllCustomers();
}
}

View File

@ -0,0 +1,60 @@
package com.example.ipLab.StoreDataBase.Controllers;
import com.example.ipLab.StoreDataBase.DTO.OrderedDTO;
import com.example.ipLab.StoreDataBase.Model.Ordered;
import com.example.ipLab.StoreDataBase.Service.CustomerService;
import com.example.ipLab.StoreDataBase.Service.OrderService;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import com.example.ipLab.WebConfiguration;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/order")
public class OrderedController {
private final OrderService orderedService;
private final ProductService productService;
private final CustomerService customerService;
public OrderedController(OrderService orderedService, ProductService productService, CustomerService customerService){
this.productService = productService;
this.customerService = customerService;
this.orderedService = orderedService;
}
@GetMapping("/{id}")
public OrderedDTO getOrdered(@PathVariable Long id){
return new OrderedDTO(orderedService.getOrder(id));
}
@GetMapping
public List<OrderedDTO> getOrdereds(){
return orderedService.getAllOrders().stream()
.map(OrderedDTO::new)
.toList();
}
@PostMapping
public OrderedDTO createOrdered(@RequestBody @Valid OrderedDTO orderedDTO){
final Ordered ordered = orderedService.addOrder(productService.getProduct(orderedDTO.getProductId()), customerService.getCustomer(orderedDTO.getCustomerId()), orderedDTO.quantity);
return new OrderedDTO(ordered);
}
@PutMapping("/{id}")
public OrderedDTO updateOrdered(@RequestBody @Valid OrderedDTO orderedDTO,
@PathVariable Long id){
return new OrderedDTO(orderedService.updateOrder(id, orderedDTO.quantity));
}
@DeleteMapping("/{id}")
public OrderedDTO deleteOrdered(@PathVariable Long id){
return new OrderedDTO(orderedService.deleteOrder(id));
}
@DeleteMapping()
public void deleteAllOrdereds(){
orderedService.deleteAllOrders();
}
}

View File

@ -0,0 +1,68 @@
package com.example.ipLab.StoreDataBase.Controllers;
import com.example.ipLab.StoreDataBase.DTO.ProductDTO;
import com.example.ipLab.StoreDataBase.Model.Product;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import com.example.ipLab.WebConfiguration;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/product")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService){
this.productService = productService;
}
@GetMapping("/{id}")
public ProductDTO getProduct(@PathVariable Long id){
return new ProductDTO(productService.getProduct(id));
}
@GetMapping
public List<ProductDTO> getProducts(){
return productService.getAllProducts().stream()
.map(ProductDTO::new)
.toList();
}
@GetMapping("/getWithStores")
public List<ProductDTO> getProductsWithStores(){
return productService.getAllProductsWithStores().stream()
.map(ProductDTO::new)
.toList();
}
@GetMapping("/getWithoutStores")
public List<ProductDTO> getProductsWithoutStores(){
return productService.getAllProductsWithoutStores().stream()
.map(ProductDTO::new)
.toList();
}
@PostMapping
public ProductDTO createProduct(@RequestBody @Valid ProductDTO productDTO){
final Product product = productService.addProduct(productDTO.getproductName());
return new ProductDTO(product);
}
@PutMapping("/{id}")
public ProductDTO updateProduct(@RequestBody @Valid ProductDTO productDTO,
@PathVariable Long id){
return new ProductDTO(productService.updateProduct(id, productDTO.getproductName()));
}
@DeleteMapping("/{id}")
public ProductDTO deleteProduct(@PathVariable Long id){
return new ProductDTO(productService.deleteProduct(id));
}
@DeleteMapping()
public void deleteAllProducts(){
productService.deleteAllProducts();
}
}

View File

@ -0,0 +1,62 @@
package com.example.ipLab.StoreDataBase.Controllers;
import com.example.ipLab.StoreDataBase.DTO.CustomerDTO;
import com.example.ipLab.StoreDataBase.DTO.ProductDTO;
import com.example.ipLab.StoreDataBase.DTO.StoreDTO;
import com.example.ipLab.StoreDataBase.Model.Store;
import com.example.ipLab.StoreDataBase.Service.StoreService;
import com.example.ipLab.WebConfiguration;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(WebConfiguration.REST_API + "/store")
public class StoreController {
private final StoreService storeService;
public StoreController(StoreService storeService){
this.storeService = storeService;
}
@GetMapping("/{id}")
public StoreDTO getStore(@PathVariable Long id){
return new StoreDTO(storeService.getStore(id));
}
@GetMapping
public List<StoreDTO> getStores(){
return storeService.getAllStores().stream()
.map(StoreDTO::new)
.toList();
}
@PostMapping
public StoreDTO createStore(@RequestBody @Valid StoreDTO storeDTO){
final Store store = storeService.addStore(storeDTO.getstoreName());
return new StoreDTO(store);
}
@PutMapping("/{id}")
public StoreDTO updateStore(@RequestBody @Valid StoreDTO storeDTO,
@PathVariable Long id){
return new StoreDTO(storeService.updateStore(id, storeDTO.getstoreName()));
}
@PutMapping("{id}/add")
public ProductDTO addProduct(@PathVariable Long id,
@RequestBody @Valid CustomerDTO customerDTO){
return new ProductDTO(storeService.addProduct(id, customerDTO.getId()));
}
@DeleteMapping("/{id}")
public StoreDTO deleteStore(@PathVariable Long id){
return new StoreDTO(storeService.deleteStore(id));
}
@DeleteMapping()
public void deleteAllStores(){
storeService.deleteAllStores();
}
}

View File

@ -0,0 +1,60 @@
package com.example.ipLab.StoreDataBase.DTO;
import com.example.ipLab.StoreDataBase.Model.Customer;
import jakarta.validation.constraints.NotBlank;
public class CustomerDTO {
public Long id;
@NotBlank(message = "lastName can't be null or empty")
public String lastName;
@NotBlank(message = "firstName can't be null or empty")
public String firstName;
@NotBlank(message = "middleName can't be null or empty")
public String middleName;
public String customerFIO;
public CustomerDTO(){
}
public CustomerDTO(Customer customer){
this.id = customer.getId();
this.lastName = customer.getLastName();
this.firstName = customer.getFirstName();
this.middleName = customer.getMiddleName();
this.customerFIO = lastName + " " + firstName + " " + middleName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getlastName() {
return lastName;
}
public void setlastName(String lastName) {
this.lastName = lastName;
}
public String getfirstName() {
return firstName;
}
public void setfirstName(String firstName) {
this.firstName = firstName;
}
public String getmiddleName() {
return middleName;
}
public void setmiddleName(String middleName) {
this.middleName = middleName;
}
public String getcustomerFIO() {
return customerFIO;
}
public void setcustomerFIO(String customerFIO) {
this.customerFIO = customerFIO;
}
}

View File

@ -0,0 +1,69 @@
package com.example.ipLab.StoreDataBase.DTO;
import com.example.ipLab.StoreDataBase.Model.Customer;
import com.example.ipLab.StoreDataBase.Model.Ordered;
import com.example.ipLab.StoreDataBase.Model.Product;
public class OrderedDTO {
public Long id;
public int quantity;
public String productName;
public String customerFIO;
public String storeName;
public Long customerId;
public Long productId;
public OrderedDTO(){
}
public OrderedDTO(Ordered ordered){
this.id = ordered.getId();
this.quantity = ordered.getQuantity();
this.productName = ordered.getProduct().getName();
this.storeName = ordered.getProduct().getStore().getStoreName();
this.customerFIO = ordered.getCustomer().getLastName() + " " + ordered.getCustomer().getFirstName() + " " + ordered.getCustomer().getMiddleName();
this.customerId = ordered.getProduct().getId();
this.productId = ordered.getProduct().getId();
}
public Long getId() {
return id;
}
public int getQuantity() {
return quantity;
}
public String getproductName() {
return productName;
}
public void setproductName(String productName) {
this.productName = productName;
}
public String getcustomerFIO() {
return customerFIO;
}
public void setcustomerFIO(String customerFIO) {
this.customerFIO = customerFIO;
}
public String getstoreName() {
return storeName;
}
public void setstoreName(String storeName) {
this.storeName = storeName;
}
public Long getCustomerId() {
return customerId;
}
public Long getProductId() {
return productId;
}
}

View File

@ -0,0 +1,34 @@
package com.example.ipLab.StoreDataBase.DTO;
import com.example.ipLab.StoreDataBase.Model.Product;
public class ProductDTO {
public Long id;
public String productName;
public String storeName;
public ProductDTO(){
}
public ProductDTO(Product product){
this.id = product.getId();
this.productName = product.getName();
this.storeName = product.getStore() == null ? null : product.getStore().getStoreName();
}
public Long getId() {
return id;
}
public String getproductName() {
return productName;
}
public void setproductName(String productName) {
this.productName = productName;
}
public String getStoreName() {
return storeName;
}
}

View File

@ -0,0 +1,37 @@
package com.example.ipLab.StoreDataBase.DTO;
import com.example.ipLab.StoreDataBase.Model.Store;
import java.util.List;
public class StoreDTO {
public Long id;
public String storeName;
public List<ProductDTO> products;
public StoreDTO(){
}
public StoreDTO(Store store){
this.id = store.getId();
this.storeName = store.getStoreName();
this.products = store.getProducts().stream().map(ProductDTO::new).toList();
}
public Long getId() {
return id;
}
public String getstoreName() {
return storeName;
}
public void setstoreName(String storeName) {
this.storeName = storeName;
}
public List<ProductDTO> getProducts() {
return products;
}
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Exceptions;
public class CustomerNotFoundException extends RuntimeException{
public CustomerNotFoundException(Long id){
super(String.format("Customer with id: %s hasn't been found", id));
}
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Exceptions;
public class OrderedNotFoundException extends RuntimeException{
public OrderedNotFoundException(Long id){
super(String.format("Order with id: %s hasn't been found", id));
}
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Exceptions;
public class ProductNotFoundException extends RuntimeException{
public ProductNotFoundException(Long id){
super(String.format("Product with id: %s hasn't been found", id));
}
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Exceptions;
public class StoreNotFoundException extends RuntimeException{
public StoreNotFoundException(Long id){
super(String.format("Store with id: %s hasn't been found", id));
}
}

View File

@ -0,0 +1,63 @@
package com.example.ipLab.StoreDataBase.MVC;
import com.example.ipLab.StoreDataBase.DTO.CustomerDTO;
import com.example.ipLab.StoreDataBase.Service.CustomerService;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/customer")
public class CustomerMVCController {
private final CustomerService customerService;
public CustomerMVCController(CustomerService customerService) {
this.customerService = customerService;
}
@GetMapping
public String getCustomers(Model model) {
model.addAttribute("customers",
customerService.getAllCustomers().stream()
.map(CustomerDTO::new)
.toList());
return "customer";
}
@GetMapping(value = {"/edit/", "/edit/{id}"})
public String editCustomer(@PathVariable(required = false) Long id,
Model model) {
if (id == null || id <= 0) {
model.addAttribute("customerDTO", new CustomerDTO());
} else {
model.addAttribute("customerId", id);
model.addAttribute("customerDTO", new CustomerDTO(customerService.getCustomer(id)));
}
return "customer-edit";
}
@PostMapping(value = {"/", "/{id}"})
public String saveCustomer(@PathVariable(required = false) Long id,
@ModelAttribute @Valid CustomerDTO customerDto,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "customer-edit";
}
if (id == null || id <= 0) {
customerService.addCustomer(customerDto.getlastName(), customerDto.getfirstName(), customerDto.getmiddleName());
} else {
customerService.updateCustomer(id, customerDto.getlastName(), customerDto.getfirstName(), customerDto.getmiddleName());
}
return "redirect:/customer";
}
@PostMapping("/delete/{id}")
public String deleteCustomer(@PathVariable Long id) {
customerService.deleteCustomer(id);
return "redirect:/customer";
}
}

View File

@ -0,0 +1,65 @@
package com.example.ipLab.StoreDataBase.MVC;
import com.example.ipLab.StoreDataBase.DTO.CustomerDTO;
import com.example.ipLab.StoreDataBase.DTO.OrderedDTO;
import com.example.ipLab.StoreDataBase.DTO.ProductDTO;
import com.example.ipLab.StoreDataBase.Service.CustomerService;
import com.example.ipLab.StoreDataBase.Service.OrderService;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/order")
public class OrderedMVCController {
private final OrderService orderedService;
private final ProductService productService;
private final CustomerService customerService;
public OrderedMVCController(OrderService orderedService, ProductService productService, CustomerService customerService){
this.productService = productService;
this.customerService = customerService;
this.orderedService = orderedService;
}
@GetMapping
public String getOrdereds(Model model) {
model.addAttribute("orders",
orderedService.getAllOrders().stream()
.map(OrderedDTO::new)
.toList());
return "order";
}
@GetMapping(value = {"/edit/", "/edit/{id}"})
public String editOrdered(@PathVariable(required = false) Long id,
Model model) {
model.addAttribute("orderDTO", new OrderedDTO());
model.addAttribute("customers", customerService.getAllCustomers().stream().map(CustomerDTO::new).toList());
model.addAttribute("products", productService.getAllProductsWithStores().stream().map(ProductDTO::new).toList());
return "order-edit";
}
@PostMapping(value = {"/", "/{id}"})
public String saveOrdered(@RequestParam(value = "productId") Long productId,
@RequestParam(value = "customerId") Long customerId,
@ModelAttribute @Valid OrderedDTO orderedDto,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "ordered-edit";
}
orderedService.addOrder(productService.getProduct(productId), customerService.getCustomer(customerId), orderedDto.getQuantity());
return "redirect:/order";
}
@PostMapping("/delete/{id}")
public String deleteOrdered(@PathVariable Long id) {
orderedService.deleteOrder(id);
return "redirect:/order";
}
}

View File

@ -0,0 +1,63 @@
package com.example.ipLab.StoreDataBase.MVC;
import com.example.ipLab.StoreDataBase.DTO.ProductDTO;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/product")
public class ProductMVCController {
private final ProductService productService;
public ProductMVCController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public String getProducts(Model model) {
model.addAttribute("products",
productService.getAllProducts().stream()
.map(ProductDTO::new)
.toList());
return "product";
}
@GetMapping(value = {"/edit/", "/edit/{id}"})
public String editProduct(@PathVariable(required = false) Long id,
Model model) {
if (id == null || id <= 0) {
model.addAttribute("productDTO", new ProductDTO());
} else {
model.addAttribute("productId", id);
model.addAttribute("productDTO", new ProductDTO(productService.getProduct(id)));
}
return "product-edit";
}
@PostMapping(value = {"/", "/{id}"})
public String saveProduct(@PathVariable(required = false) Long id,
@ModelAttribute @Valid ProductDTO productDto,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "product-edit";
}
if (id == null || id <= 0) {
productService.addProduct(productDto.getproductName());
} else {
productService.updateProduct(id, productDto.getproductName());
}
return "redirect:/product";
}
@PostMapping("/delete/{id}")
public String deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
return "redirect:/product";
}
}

View File

@ -0,0 +1,84 @@
package com.example.ipLab.StoreDataBase.MVC;
import com.example.ipLab.StoreDataBase.DTO.CustomerDTO;
import com.example.ipLab.StoreDataBase.DTO.ProductDTO;
import com.example.ipLab.StoreDataBase.DTO.StoreDTO;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import com.example.ipLab.StoreDataBase.Service.StoreService;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/store")
public class StoreMVCController {
private final StoreService storeService;
private final ProductService productService;
public StoreMVCController(StoreService storeService, ProductService productService) {
this.storeService = storeService;
this.productService = productService;
}
@GetMapping
public String getStores(Model model) {
model.addAttribute("stores",
storeService.getAllStores().stream()
.map(StoreDTO::new)
.toList());
return "store";
}
@GetMapping(value = {"/edit/", "/edit/{id}"})
public String editStore(@PathVariable(required = false) Long id,
Model model) {
if (id == null || id <= 0) {
model.addAttribute("storeDTO", new StoreDTO());
} else {
model.addAttribute("storeId", id);
model.addAttribute("storeDTO", new StoreDTO(storeService.getStore(id)));
}
return "store-edit";
}
@GetMapping(value = "/addToStore")
public String addToStore(@PathVariable(required = false) Long id,
Model model) {
model.addAttribute("stores", storeService.getAllStores().stream().map(StoreDTO::new).toList());
model.addAttribute("products", productService.getAllProductsWithoutStores().stream().map(ProductDTO::new).toList());
return "addToStore";
}
@PostMapping(value = {"/", "/{id}"})
public String saveStore(@PathVariable(required = false) Long id,
@ModelAttribute @Valid StoreDTO storeDto,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "store-edit";
}
if (id == null || id <= 0) {
storeService.addStore(storeDto.getstoreName());
} else {
storeService.updateStore(id, storeDto.getstoreName());
}
return "redirect:/store";
}
@PostMapping("/add")
public String addProduct(@RequestParam(value = "storeId") Long storeId,
@RequestParam(value = "productId") Long productId
){
storeService.addProduct(storeId, productId);
return "redirect:/product";
}
@PostMapping("/delete/{id}")
public String deleteStore(@PathVariable Long id) {
storeService.deleteStore(id);
return "redirect:/store";
}
}

View File

@ -0,0 +1,95 @@
package com.example.ipLab.StoreDataBase.Model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
@NotBlank(message = "Customer's last name can't be empty")
private String lastName;
@Column
@NotBlank(message = "Customer's first name can't be empty")
private String firstName;
@Column
@NotBlank(message = "Customer's middle name can't be empty")
private String middleName;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "customer", cascade = CascadeType.ALL)
private List<Ordered> orders;
public void AddOrdered(Ordered ordered){
this.orders.add(ordered);
if (ordered.getCustomer() != this){
ordered.setCustomer(this);
}
}
@PreRemove
public void removeOrders(){
for (var order:
orders) {
order.removeCustomer();
}
orders = null;
}
public Customer(){
this.orders = new ArrayList<>();
}
public Customer(String lastName, String firstName, String middleName){
this.lastName = lastName;
this.firstName = firstName;
this.middleName = middleName;
this.orders = new ArrayList<Ordered>();
}
public Long getId() {
return id;
}
public String getLastName() {
return lastName;
}
public String getMiddleName() {
return middleName;
}
public String getFirstName() {
return firstName;
}
public List<Ordered> getOrders() {
return orders;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setMiddleName(String middleName) {
this.middleName = middleName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Customer customer = (Customer) obj;
return Objects.equals(id, customer.id);
}
@Override
public int hashCode(){
return Objects.hashCode(id);
}
}

View File

@ -0,0 +1,82 @@
package com.example.ipLab.StoreDataBase.Model;
import jakarta.persistence.*;
import java.util.Objects;
@Entity
public class Ordered {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="customer_fk")
private Customer customer;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="product_fk")
private Product product;
@Column
private int quantity;
public Ordered(){}
public Ordered(int quantity){
this.quantity = quantity;
}
public Long getId() {
return id;
}
public Customer getCustomer() {
return customer;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
if (!product.getOrders().contains(this)){
product.AddOrdered(this);
}
}
@PreRemove
public void removeProduct(){
this.product.getOrders().remove(this);
this.product = null;
removeCustomer();
}
public void removeCustomer(){
this.customer.getOrders().remove(this);
this.customer = null;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public void setCustomer(Customer customer) {
this.customer = customer;
if (!customer.getOrders().contains(this)){
customer.AddOrdered(this);
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Ordered order = (Ordered) obj;
return Objects.equals(id, order.id);
}
@Override
public int hashCode(){
return Objects.hashCode(id);
}
}

View File

@ -0,0 +1,94 @@
package com.example.ipLab.StoreDataBase.Model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
@NotBlank(message = "Product's name can't be empty")
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "store_fk")
private Store store;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "product", cascade = CascadeType.ALL)
private List<Ordered> orders;
public Product(){}
public Product(String name){
this.name = name;
this.orders = new ArrayList<Ordered>();
}
public Product(Store store, String name){
this.store = store;
this.name = name;
this.orders = new ArrayList<Ordered>();
}
public void AddOrdered(Ordered ordered){
this.orders.add(ordered);
if (ordered.getProduct() != this){
ordered.setProduct(this);
}
}
@PreRemove
public void removeStore(){
if (this.store != null) {
this.store.getProducts().remove(this);
this.store = null;
}
removeOrders();
}
public void removeOrders(){
for (var order:
orders) {
order.removeProduct();
}
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Store getStore() {
return store;
}
public List<Ordered> getOrders() {
return orders;
}
public void setName(String name) {
this.name = name;
}
public void setStore(Store store) {
this.store = store;
if (!store.getProducts().contains(this)){
store.AddProduct(this);
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Product product = (Product) obj;
return Objects.equals(id, product.id);
}
@Override
public int hashCode(){
return Objects.hashCode(id);
}
}

View File

@ -0,0 +1,70 @@
package com.example.ipLab.StoreDataBase.Model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
public class Store {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column
@NotBlank(message = "Store's name can't be empty")
private String storeName;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "store", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products;
public Store(){
this.products = new ArrayList<>();
}
public Store(String storeName){
this.storeName = storeName;
this.products = new ArrayList<>();
}
public Long getId() {
return id;
}
public String getStoreName() {
return storeName;
}
public List<Product> getProducts() {
return products;
}
public void AddProduct(Product product){
this.products.add(product);
if (product.getStore() != this){
product.setStore(this);
}
}
@PreRemove
public void removeProducts(){
for (var product:
products) {
product.removeStore();
}
products = new ArrayList<>();
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Store store = (Store) obj;
return Objects.equals(id, store.id);
}
@Override
public int hashCode(){
return Objects.hashCode(id);
}
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Repositories;
import com.example.ipLab.StoreDataBase.Model.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Repositories;
import com.example.ipLab.StoreDataBase.Model.Ordered;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderedRepository extends JpaRepository<Ordered, Long> {
}

View File

@ -0,0 +1,14 @@
package com.example.ipLab.StoreDataBase.Repositories;
import com.example.ipLab.StoreDataBase.Model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.Collection;
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query("SELECT p FROM Product p WHERE p.store <> null")
Collection<Product> findAllProductsWithStores();
@Query("SELECT p FROM Product p WHERE p.store = null")
Collection<Product> findAllProductsWithoutStores();
}

View File

@ -0,0 +1,7 @@
package com.example.ipLab.StoreDataBase.Repositories;
import com.example.ipLab.StoreDataBase.Model.Store;
import org.springframework.data.jpa.repository.JpaRepository;
public interface StoreRepository extends JpaRepository<Store, Long> {
}

View File

@ -0,0 +1,68 @@
package com.example.ipLab.StoreDataBase.Service;
import com.example.ipLab.StoreDataBase.Exceptions.CustomerNotFoundException;
import com.example.ipLab.StoreDataBase.Model.Customer;
import com.example.ipLab.StoreDataBase.Repositories.CustomerRepository;
import com.example.ipLab.StoreDataBase.util.validation.ValidatorUtil;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
private final ValidatorUtil validatorUtil;
public CustomerService(CustomerRepository customerRepository,
ValidatorUtil validatorUtil){
this.customerRepository = customerRepository;
this.validatorUtil = validatorUtil;
}
@Transactional
public Customer addCustomer(String customerLastName, String customerFirstName, String customerMiddleName){
Customer customer = new Customer(customerLastName, customerFirstName, customerMiddleName);
validatorUtil.validate(customer);
return customerRepository.save(customer);
}
@Transactional()
public Customer getCustomer(Long id){
return customerRepository.findById(id).orElseThrow(() -> new CustomerNotFoundException(id));
}
@Transactional
public List<Customer> getAllCustomers(){
return customerRepository.findAll();
}
@Transactional
public Customer updateCustomer(Long id, String customerLastName, String customerFirstName, String customerMiddleName){
Customer customer = getCustomer(id);
customer.setLastName(customerLastName);
customer.setFirstName(customerFirstName);
customer.setMiddleName(customerMiddleName);
validatorUtil.validate(customer);
return customerRepository.save(customer);
}
@Transactional
public Customer deleteCustomer(Long id){
Customer customer = getCustomer(id);
customerRepository.delete(customer);
return customer;
}
@Transactional
public void deleteAllCustomers(){
//for (var customer:
// getAllCustomers()) {
// customer.removeOrders();
//}
customerRepository.deleteAll();
}
}

View File

@ -0,0 +1,86 @@
package com.example.ipLab.StoreDataBase.Service;
import com.example.ipLab.StoreDataBase.Exceptions.OrderedNotFoundException;
import com.example.ipLab.StoreDataBase.Model.Customer;
import com.example.ipLab.StoreDataBase.Model.Ordered;
import com.example.ipLab.StoreDataBase.Model.Product;
import com.example.ipLab.StoreDataBase.Model.Store;
import com.example.ipLab.StoreDataBase.Repositories.OrderedRepository;
import com.example.ipLab.StoreDataBase.util.validation.ValidatorUtil;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.criteria.Order;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class OrderService {
private final OrderedRepository orderedRepository;
private final ValidatorUtil validatorUtil;
public OrderService(OrderedRepository orderedRepository,
ValidatorUtil validatorUtil,
ProductService productService){
this.productService = productService;
this.orderedRepository = orderedRepository;
this.validatorUtil = validatorUtil;
}
private ProductService productService;
@Transactional
public Ordered addOrder(Product product, Customer customer, int quantity){
final Ordered order = new Ordered(quantity);
validatorUtil.validate(order);
product.AddOrdered(order);
customer.AddOrdered(order);
orderedRepository.save(order);
return order;
}
@Transactional()
public Ordered getOrder(Long id){
return orderedRepository.findById(id).orElseThrow(() -> new OrderedNotFoundException(id));
}
@Transactional
public List<Ordered> getAllOrders(){
return orderedRepository.findAll();
}
@Transactional
public Ordered updateOrder(Long id, int quantity){
final Ordered order = getOrder(id);
order.setQuantity(quantity);
validatorUtil.validate(order);
return orderedRepository.save(order);
}
@Transactional
public Ordered deleteOrder(Long id){
final Ordered order = getOrder(id);
order.getCustomer().getOrders().remove(order);
orderedRepository.delete(order);
return order;
}
@Transactional
public void deleteAllOrders(){
//for (var order:
// getAllOrders()) {
// order.removeProduct();
// order.removeCustomer();
//}
orderedRepository.deleteAll();
}
//product section
@Transactional()
public Product getProduct(Long orderId){
Ordered order = getOrder(orderId);
return order.getProduct();
}
}

View File

@ -0,0 +1,78 @@
package com.example.ipLab.StoreDataBase.Service;
import com.example.ipLab.StoreDataBase.Exceptions.ProductNotFoundException;
import com.example.ipLab.StoreDataBase.Model.Customer;
import com.example.ipLab.StoreDataBase.Model.Ordered;
import com.example.ipLab.StoreDataBase.Model.Product;
import com.example.ipLab.StoreDataBase.Model.Store;
import com.example.ipLab.StoreDataBase.Repositories.ProductRepository;
import com.example.ipLab.StoreDataBase.util.validation.ValidatorUtil;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Service
public class ProductService {
private final ProductRepository productRepository;
private final ValidatorUtil validatorUtil;
public ProductService(ProductRepository productRepository, ValidatorUtil validatorUtil){
this.productRepository = productRepository;
this.validatorUtil = validatorUtil;
}
@Transactional
public Product addProduct(String productName){
final Product product = new Product(productName);
validatorUtil.validate(product);
productRepository.save(product);
return product;
}
@Transactional()
public Product getProduct(Long id){
return productRepository.findById(id).orElseThrow(() -> new ProductNotFoundException(id));
}
@Transactional
public List<Product> getAllProducts(){
return productRepository.findAll();
}
@Transactional
public List<Product> getAllProductsWithStores(){
return productRepository.findAllProductsWithStores().stream().toList();
}
@Transactional
public List<Product> getAllProductsWithoutStores(){
return productRepository.findAllProductsWithoutStores().stream().toList();
}
@Transactional
public Product updateProduct(Long id, String productName){
final Product product = getProduct(id);
product.setName(productName);
validatorUtil.validate(product);
return productRepository.save(product);
}
@Transactional
public Product deleteProduct(Long id){
final Product product = getProduct(id);
Store store = product.getStore();
if (store != null) store.getProducts().remove(product);
productRepository.delete(product);
return product;
}
@Transactional
public void deleteAllProducts(){
//for (var product:
// getAllProducts()) {
// product.removeStore();
// product.removeOrders();
//}
productRepository.deleteAll();
}
}

View File

@ -0,0 +1,115 @@
package com.example.ipLab.StoreDataBase.Service;
import com.example.ipLab.StoreDataBase.Exceptions.StoreNotFoundException;
import com.example.ipLab.StoreDataBase.Model.Product;
import com.example.ipLab.StoreDataBase.Model.Store;
import com.example.ipLab.StoreDataBase.Repositories.StoreRepository;
import com.example.ipLab.StoreDataBase.util.validation.ValidatorUtil;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class StoreService {
private final StoreRepository storeRepository;
private final ValidatorUtil validatorUtil;
private ProductService productService;
public StoreService(StoreRepository storeRepository, ValidatorUtil validatorUtil, ProductService productService){
this.storeRepository = storeRepository;
this.validatorUtil = validatorUtil;
this.productService = productService;
}
@Transactional
public Store addStore(String storeName){
final Store store = new Store(storeName);
validatorUtil.validate(store);
storeRepository.save(store);
return store;
}
@Transactional()
public Store getStore(Long id){
return storeRepository.findById(id).orElseThrow(() -> new StoreNotFoundException(id));
}
@Transactional
public List<Store> getAllStores(){
return storeRepository.findAll();
}
@Transactional
public Store updateStore(Long id, String storeName){
final Store store = getStore(id);
store.setStoreName(storeName);
validatorUtil.validate(store);
return storeRepository.save(store);
}
@Transactional
public Store deleteStore(Long id){
final Store store = getStore(id);
storeRepository.delete(store);
return store;
}
@Transactional
public void deleteAllStores(){
//for (var store:
// getAllStores()) {
// store.removeProducts();
//}
storeRepository.deleteAll();
}
//product section
@Transactional
public Product addProduct(Long storeId, Long productId){
Store store = getStore(storeId);
Product product = productService.getProduct(productId);
store.AddProduct(product);
storeRepository.save(store);
return product;
}
@Transactional()
public Product getProductFromStore(Long productId, Long storeId){
Store store = getStore(storeId);
var prFind = store.getProducts().stream().filter(pr -> pr.getId().equals(productId)).findFirst();
if (prFind.isPresent()) {
return prFind.get();
}
else throw new EntityNotFoundException(String.format("Product with id = %s isn't found in store with id = %s", productId, storeId));
}
@Transactional
public List<Product> getAllProductsFromStore(Long storeId) {
Store store = getStore(storeId);
return store.getProducts();
}
// @Transactional
// public Product deleteProductFromStore(Long storeId, Long productId){
// Store store = getStore(storeId);
// Product product = getProductFromStore(productId, storeId);
// store.getProducts().remove(product);
// product.setStore(null);
// return product;
// }
// @Transactional
// public void deleteAllProducts(Long storeId){
// Store store = getStore(storeId);
// List<Product> storeProducts = store.getProducts();
// for (Product pr:
// storeProducts) {
// pr.setStore(null);
// store.getProducts().remove(pr);
// }
// }
}

View File

@ -0,0 +1,43 @@
package com.example.ipLab.StoreDataBase.util.error;
import com.example.ipLab.StoreDataBase.Exceptions.CustomerNotFoundException;
import com.example.ipLab.StoreDataBase.Exceptions.OrderedNotFoundException;
import com.example.ipLab.StoreDataBase.Exceptions.ProductNotFoundException;
import com.example.ipLab.StoreDataBase.Exceptions.StoreNotFoundException;
import com.example.ipLab.StoreDataBase.util.validation.ValidationException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import java.util.stream.Collectors;
@ControllerAdvice(annotations = RestController.class)
public class AdviceController {
@ExceptionHandler({
CustomerNotFoundException.class,
OrderedNotFoundException.class,
ProductNotFoundException.class,
StoreNotFoundException.class
})
public ResponseEntity<Object> handleException(Throwable e){
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleBindException(MethodArgumentNotValidException e) {
final ValidationException validationException = new ValidationException(
e.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toSet()));
return handleException(validationException);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleUnknownException(Throwable e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}

View File

@ -0,0 +1,9 @@
package com.example.ipLab.StoreDataBase.util.validation;
import java.util.Set;
public class ValidationException extends RuntimeException{
public ValidationException(Set<String> errors){
super(String.join("\n", errors));
}
}

View File

@ -0,0 +1,30 @@
package com.example.ipLab.StoreDataBase.util.validation;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.stream.Collectors;
@Component
public class ValidatorUtil {
private final Validator validator;
public ValidatorUtil(){
try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()){
this.validator = factory.getValidator();
}
}
public <T> void validate(T object) {
final Set<ConstraintViolation<T>> errors = validator.validate(object);
if (!errors.isEmpty()) {
throw new ValidationException(errors.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.toSet()));
}
}
}

View File

@ -6,6 +6,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration @Configuration
public class WebConfiguration implements WebMvcConfigurer { public class WebConfiguration implements WebMvcConfigurer {
public static final String REST_API = "/api";
@Override @Override
public void addCorsMappings(CorsRegistry registry){ public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**").allowedMethods("*"); registry.addMapping("/**").allowedMethods("*");

View File

@ -1,39 +0,0 @@
package com.example.ipLab.controllers;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CalcController {
@GetMapping("/second")
public int second(@RequestParam(value = "num") int num){
return num*num;
}
@GetMapping("/root")
public double root(@RequestParam(value = "num") int num){
return Math.sqrt(num);
}
@GetMapping("/fact")
public int fact(@RequestParam(value = "num") int num){
int res = 1;
for (int i = 2; i <= num; i++) {
res *= i;
}
return res;
}
@GetMapping("/digit")
public int digit(@RequestParam(value = "num") int num){
if (num < 0) num *= -1;
int sum = 0;
while (num > 0) {
sum += num % 10;
num /= 10;
}
return sum;
}
}

View File

@ -1 +1,11 @@
spring.main.banner-mode=off
#server.port=8080
spring.datasource.url=jdbc:h2:file:./data
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false

View File

@ -0,0 +1,284 @@
header{
background-color: #9094c1;
flex-direction: row;
}
header nav{
font-family: Segoe UI;
font-weight: bold;
font-size: 42px;
}
header nav a{
color: inherit;
}
header nav a:hover{
text-decoration: underline;
}
.navigationCaption{
font-family: Segoe UI;
font-weight: bold;
font-size: 42px;
}
.headNav{
color: inherit;
}
a{
color: inherit;
text-decoration: none;
}
td, th{
border: 1px solid black;
font-family: Segoe UI;
text-align: center;
font-size: 28px;
}
table tbody td a:hover{
text-decoration: underline;
color: inherit;
}
footer{
background-color: #707be5;
max-height: 110px;
}
.mainPage a:hover{
text-decoration: underline;
color: inherit;
}
.popularCaption{
font-family: Segoe UI;
font-size: 24px;
}
.discountsCaption{
font-family: Segoe UI;
font-size: 24px;
}
.item{
font-family: Segoe UI;
font-size: 18px;
}
.item img{
width: 200px;
height: 200px;
}
.tableMy table caption{
font-family: Segoe UI;
font-weight: bold;
font-size: 45px;
}
.tableMy table tbody td a:hover{
text-decoration: underline;
color: inherit !important;
}
.tableCart table caption{
font-family: Segoe UI;
font-weight: bold;
font-size: 32px;
}
.cartInfo{
margin-top: 320px;
margin-right: 400px;
font-family: Segoe UI;
font-size: 45px;
}
.buttonOrder{
background-color: #4d89d9;
}
.itemCaption{
font-family: Segoe UI;
font-size: 32px;
}
#itemPhoto{
margin-left: 50px;
}
.itemInfo{
font-family: Segoe UI;
font-size: 45px;
margin-left: 85px;
}
.itemInfo li{
list-style-type: none;
}
.buttonAdd{
font-family: Segoe UI;
font-size: 45px;
background-color: #4d89d9;
margin-left: 35px;
}
.companyName{
font-family: Segoe UI;
font-size: 45px;
}
.longText{
font-family: Segoe UI;
font-size: 25px;
}
#logoName{
font-family: Rockwell Condensed;
font-size: 64px;
font-weight: bold;
font-stretch: condensed;
text-decoration: none;
}
#logoName a:hover{
text-decoration: none;
color: inherit;
}
.footerLeft{
margin-bottom: 10px;
font-family: Segoe UI;
font-size: 16px;
}
.footerBottom{
font-family: Segoe UI;
font-size: 28px;
}
.hide{
display: none;
}
.active{
display: block;
}
.active img{
width: 200px;
height: 100px;
}
@media (max-width: 1080px){
header{
flex-direction: column;
}
header nav{
text-align: center;
}
.headerContainer{
flex-direction: column !important;
}
.itemContent{
flex-direction: column !important;
justify-content: center !important;
}
#itemPhoto{
margin-left: auto !important;
margin-right: auto ;
}
.itemInfo{
margin-bottom: 10px !important;
margin-left: 10px!important;
}
#cartContainer{
flex-direction: column !important;
}
.cartInfo{
margin-top: 0px !important;
margin-left: 5px;
margin-right: auto;
margin-bottom: 10px;
}
#tableCart{
width: auto;
}
.mainPage{
flex-direction: column !important;
justify-content: center;
}
.tablePopular{
margin-left: auto !important;
margin-right: auto !important;
}
.tableDiscounts{
margin-top: 30px;
margin-left: auto !important;
margin-right: auto !important;
}
}
@media (max-width: 767px){
.tableMy table thead th{
font-size: 12px !important;
}
.tableMy table tr td{
font-size: 12px !important;
}
.tableCart table thead th{
font-size: 18px !important;
}
.tableCart table tr td{
font-size: 18px !important;
}
.cartInfo{
font-size: 18px !important;
}
.itemInfo{
font-size: 18px !important;
}
.buttonAdd{
font-size: 18px !important
}
.footerLeft{
font-size: 12px !important;
}
.footerBottom{
font-size: 16px !important;
}
.footerRight img{
width: 55px;
height: 27px;
}
.mainPage img{
width: 100px !important;
height: 100px !important;
}
.popularCaption{
font-size: 18px !important;
}
.discountsCaption{
font-size: 18px !important;
}
#itemIcon{
width: 250px !important;
height: 250px !important;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/store/add}" method="post">
<div class="mb-3">
<label for="store" class="form-label">Покупатель</label>
<select id="store" class="form-select" th:name="storeId">
<option th:each="value: ${stores}" th:value="${value.id}" th:selected="${storeId} == ${value}">
<span th:text="${value.storeName}"></span>
</option>
</select>
</div>
<div class="mb-3">
<label for="product" class="form-label">Продукт</label>
<select id="product" class="form-select" th:name="productId">
<option th:each="value: ${products}" th:value="${value.id}" th:selected="${productId} == ${value}">
<span th:text="${value.productName}"></span>
</option>
</select>
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary button-fixed">
<span>Добавить</span>
</button>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/customer/{id}(id=${id})}" th:object="${customerDTO}" method="post">
<div class="mb-3">
<label for="lastName" class="form-label">Фамилия</label>
<input type="text" class="form-control" id="lastName" th:field="${customerDTO.lastName}" required="true">
</div>
<div class="mb-3">
<label for="firstName" class="form-label">Имя</label>
<input type="text" class="form-control" id="firstName" th:field="${customerDTO.firstName}" required="true">
</div>
<div class="mb-3">
<label for="middleName" class="form-label">Отчество</label>
<input type="text" class="form-control" id="middleName" th:field="${customerDTO.middleName}" required="true">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary button-fixed">
<span th:if="${id == null}">Добавить</span>
<span th:if="${id != null}">Обновить</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/customer}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed"
th:href="@{/customer/edit/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table table-success table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Фамилия</th>
<th scope="col">Имя</th>
<th scope="col">Отчество</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr th:each="customer, iterator: ${customers}">
<th scope="row" th:text="${iterator.index} + 1"/>
<td th:text="${customer.lastName}"/>
<td th:text="${customer.firstName}"/>
<td th:text="${customer.middleName}"/>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-warning button-fixed button-sm"
th:href="@{/customer/edit/{id}(id=${customer.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
</a>
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${customer.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
</div>
<form th:action="@{/customer/delete/{id}(id=${customer.id})}" method="post">
<button th:id="'remove-' + ${customer.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="ru"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8"/>
<title>IP Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="icon" href="/favicon.ico">
<script type="text/javascript" src="/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="/webjars/bootstrap/5.1.3/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/webjars/font-awesome/6.1.0/css/all.min.css"/>
<link rel="stylesheet" href="/css/styleSite.css"/>
</head>
<body>
<header>
<div class="d-flex flex-row headerContainer">
<nav class="navbar navbar-expand-md">
<div class="container-fluid" id="navigationMenu">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="d-flex flex-row ml-3 ms-3 mt-auto mb-auto align-items-center">
<a>
<img src="/logo.png" alt="*" width="60" height="60" class="align-text-top"></img>
</a>
<div id="logoName">
<a href="/">boxStore</a>
</div>
</div>
<div class="navbar-collapse collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav" id="headerNavigation">
<a class="nav-link headNav" href="/customer"
th:classappend="${#strings.equals(activeLink, '/customer')} ? 'active' : ''">Клиенты</a>
<a class="nav-link headNav" href="/store"
th:classappend="${#strings.equals(activeLink, '/store')} ? 'active' : ''">Магазины</a>
<a class="nav-link headNav" href="/product"
th:classappend="${#strings.equals(activeLink, '/product')} ? 'active' : ''">Товары</a>
<a class="nav-link headNav" href="/order"
th:classappend="${#strings.equals(activeLink, '/order')} ? 'active' : ''">Заказы</a>
<a class="nav-link headNav" href="/store/addToStore"
th:classappend="${#strings.equals(activeLink, '/order')} ? 'active' : ''">Доставка</a>
</ul>
</div>
</div>
</nav>
</div>
</header>
<div class="container-fluid">
<div class="container container-padding" layout:fragment="content">
</div>
</div>
</body>
<th:block layout:fragment="scripts">
</th:block>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div><span th:text="${error}"></span></div>
<a href="/">На главную</a>
</div>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<h2>Добро пожаловать!</h2>
</div>
</body>
</html>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/order/}" th:object="${orderDTO}" method="post">
<div class="mb-3">
<label for="customer" class="form-label">Покупатель</label>
<select id="customer" class="form-select" th:name="customerId">
<option th:each="value: ${customers}" th:value="${value.id}" th:selected="${customerId} == ${value}">
<span th:text="${value.customerFIO}"></span>
</option>
</select>
</div>
<div class="mb-3">
<label for="product" class="form-label">Продукт</label>
<select id="product" class="form-select" th:name="productId">
<option th:each="value: ${products}" th:value="${value.id}" th:selected="${productId} == ${value}">
<span th:text="${value.productName}"></span>
</option>
</select>
</div>
<div class="mb-3">
<label for="quantity" class="form-label">Количество</label>
<input type="text" class="form-control" id="quantity" th:field="${orderDTO.quantity}" required="true">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary button-fixed">
<span>Добавить</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/order}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed"
th:href="@{/order/edit/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table table-success table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">ФИО покупателя</th>
<th scope="col">Магазин</th>
<th scope="col">Товар</th>
</tr>
</thead>
<tbody>
<tr th:each="order, iterator: ${orders}">
<th scope="row" th:text="${iterator.index} + 1"/>
<td th:text="${order.customerFIO}"/>
<td th:text="${order.storeName}"/>
<td th:text="${order.productName}"/>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/product/{id}(id=${id})}" th:object="${productDTO}" method="post">
<div class="mb-3">
<label for="productName" class="form-label">Название товара</label>
<input type="text" class="form-control" id="productName" th:field="${productDTO.productName}" required="true">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary button-fixed">
<span th:if="${id == null}">Добавить</span>
<span th:if="${id != null}">Обновить</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/product}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed "
th:href="@{/product/edit/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table table-success table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Название товара</th>
<th scope="col">Название магазина</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr th:each="product, iterator: ${products}">
<th scope="row" th:text="${iterator.index} + 1"/>
<td th:text="${product.productName}"/>
<td th:text="${product.storeName}"/>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-warning button-fixed button-sm"
th:href="@{/product/edit/{id}(id=${product.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
</a>
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${product.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
</div>
<form th:action="@{/product/delete/{id}(id=${product.id})}" method="post">
<button th:id="'remove-' + ${product.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div th:text="${errors}" class="margin-bottom alert-danger"></div>
<form action="#" th:action="@{/store/{id}(id=${id})}" th:object="${storeDTO}" method="post">
<div class="mb-3">
<label for="storeName" class="form-label">Название магазина</label>
<input type="text" class="form-control" id="storeName" th:field="${storeDTO.storeName}" required="true">
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary button-fixed">
<span th:if="${id == null}">Добавить</span>
<span th:if="${id != null}">Обновить</span>
</button>
<a class="btn btn-secondary button-fixed" th:href="@{/store}">
Назад
</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}">
<head>
</head>
<body>
<div layout:fragment="content">
<div>
<a class="btn btn-success button-fixed"
th:href="@{/store/edit/}">
<i class="fa-solid fa-plus"></i> Добавить
</a>
</div>
<div class="table-responsive">
<table class="table table-success table-hover">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Название магазина</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr th:each="store, iterator: ${stores}">
<th scope="row" th:text="${iterator.index} + 1"/>
<td th:text="${store.storeName}"/>
<td style="width: 10%">
<div class="btn-group" role="group" aria-label="Basic example">
<a class="btn btn-warning button-fixed button-sm"
th:href="@{/store/edit/{id}(id=${store.id})}">
<i class="fa fa-pencil" aria-hidden="true"></i> Изменить
</a>
<button type="button" class="btn btn-danger button-fixed button-sm"
th:attr="onclick=|confirm('Удалить запись?') && document.getElementById('remove-${store.id}').click()|">
<i class="fa fa-trash" aria-hidden="true"></i> Удалить
</button>
</div>
<form th:action="@{/store/delete/{id}(id=${store.id})}" method="post">
<button th:id="'remove-' + ${store.id}" type="submit" style="display: none">
Удалить
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

View File

@ -1,13 +0,0 @@
package com.example.ipLab;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class IpLabApplicationTests {
@Test
void contextLoads() {
}
}

View File

@ -0,0 +1,158 @@
package com.example.ipLab;
import com.example.ipLab.StoreDataBase.Model.Customer;
import com.example.ipLab.StoreDataBase.Model.Ordered;
import com.example.ipLab.StoreDataBase.Model.Product;
import com.example.ipLab.StoreDataBase.Model.Store;
import com.example.ipLab.StoreDataBase.Service.CustomerService;
import com.example.ipLab.StoreDataBase.Service.OrderService;
import com.example.ipLab.StoreDataBase.Service.ProductService;
import com.example.ipLab.StoreDataBase.Service.StoreService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
public class JpaTests {
@Autowired
OrderService orderService;
@Autowired
CustomerService customerService;
@Autowired
StoreService storeService;
@Autowired
ProductService productService;
@Test
void testStore(){
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
Store store = storeService.addStore("example");
Assertions.assertEquals("example", store.getStoreName());
Assertions.assertEquals("example", storeService.getStore(store.getId()).getStoreName());
storeService.updateStore(store.getId(), "newName");
Assertions.assertEquals("newName", storeService.getStore(store.getId()).getStoreName());
Assertions.assertEquals("newName", storeService.deleteStore(store.getId()).getStoreName());
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
}
@Test
void testCustomer(){
orderService.deleteAllOrders();
var list = orderService.getAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
Customer c = customerService.addCustomer("1", "2", "3");
Assertions.assertEquals("2", c.getFirstName());
Assertions.assertEquals("2", customerService.getCustomer(c.getId()).getFirstName());
Assertions.assertEquals("1", customerService.updateCustomer(c.getId(), c.getLastName(), "1", c.getMiddleName()).getFirstName());
Assertions.assertEquals("1", customerService.deleteCustomer(c.getId()).getFirstName());
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
}
@Test
void testProduct(){
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
Store store = storeService.addStore("example");
Assertions.assertEquals("example", store.getStoreName());
Product p = productService.addProduct("product");
Assertions.assertEquals("product", p.getName());
Assertions.assertEquals("product", storeService.addProduct(store.getId(), p.getId()).getName());
Assertions.assertEquals("product", storeService.getProductFromStore(p.getId(), store.getId()).getName());
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
}
@Test
void testOrder(){
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
Store store = storeService.addStore("example");
Assertions.assertEquals("example", store.getStoreName());
Product p = productService.addProduct("product");
storeService.addProduct(store.getId(), p.getId());
Assertions.assertEquals("product", p.getName());
Customer c = customerService.addCustomer("1", "2", "3");
Assertions.assertEquals("2", c.getFirstName());
Ordered order = orderService.addOrder(p, c, 5);
Assertions.assertEquals("5", Integer.toString(order.getQuantity()));
Assertions.assertEquals("5", Integer.toString(orderService.getOrder(order.getId()).getQuantity()));
Assertions.assertEquals("6", Integer.toString(orderService.updateOrder(order.getId(), 6).getQuantity()));
Assertions.assertEquals("6", Integer.toString(orderService.deleteOrder(order.getId()).getQuantity()));
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
}
@Test
void FilterOrderTest(){
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
Store store = storeService.addStore("example");
Assertions.assertEquals("example", store.getStoreName());
Product p1 = productService.addProduct("product");
Product p2 = productService.addProduct("product2");
storeService.addProduct(store.getId(), p1.getId());
Assertions.assertEquals("product", p1.getName());
storeService.addProduct(store.getId(), p2.getId());
Assertions.assertEquals("product2", p2.getName());
Customer c = customerService.addCustomer("1", "2", "3");
Assertions.assertEquals("2", c.getFirstName());
Ordered order1 = orderService.addOrder(p1, c, 0);
Ordered order2 = orderService.addOrder(p2, c, 6);
Ordered order3 = orderService.addOrder(p1, c, 2);
Ordered order4 = orderService.addOrder(p2, c, 2);
Ordered order5 = orderService.addOrder(p1, c, 3);
//List<Ordered> expectedResult = new ArrayList<>();
//expectedResult.add(order3);
//expectedResult.add(order5);
//orderService.getAllOrders();
//Assertions.assertEquals(expectedResult, orderService.getOrdersWithProduct(p1.getId(), 1, 5));
orderService.deleteAllOrders();
customerService.deleteAllCustomers();
productService.deleteAllProducts();
storeService.deleteAllStores();
}
}

View File

@ -0,0 +1,6 @@
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop

BIN
frontend/AppStore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
frontend/GooglePlay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
frontend/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,25 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ru" class="h-100"> <html lang="ru" class="h-100">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<script src ="node_modules/bootstrap/dist/js/bootstrap.min.js"></script> <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src ="scripts/calc.js"></script> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> <link rel="stylesheet" href="/node_modules/@fortawesome/fontawesome-free/css/all.min.css">
<link href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet" /> <title>IP lab</title>
<title>Calc</title> </head>
<link rel="stylesheet" type="text/css" href="style.css"> <body class="h-100 bg-light m-0">
</head> <article class="h-100">
<body class="h-100"> <div id="app" class="h-100"></div>
<div class="d-flex flex-column align-content-center flex-wrap"> <script type="module" src="/src/main.jsx"></script>
<div class="input-group p-3"> </article>
<input id="numberInput" type="number" class="form-control" placeholder="Введите число..." required> </body>
<button class="btn btn-outline-secondary" onclick="calcSecond()" type="button">^2</button>
<button class="btn btn-outline-secondary" onclick="calcRoot()" type="button"></button>
<button class="btn btn-outline-secondary" onclick="calcFact()" type="button">!</button>
<button class="btn btn-outline-secondary" onclick="calcDigit()" type="button">Сумма цифр</button>
</div>
<a id="responseField" class="m-3"></a>
</div>
</body>
</html> </html>

BIN
frontend/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

4391
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,26 @@
{ {
"name": "ip_lab", "name": "test",
"version": "1.0.0", "version": "1.0.0",
"description": "My project for IP lab", "type": "module",
"main": "index.html",
"scripts": { "scripts": {
"start": "http-server -p 3000 ./", "dev": "vite",
"test": "echo \"Error: no test specified\" && exit 1" "start": "npm-run-all --parallel dev",
"build": "vite build",
"preview": "vite preview"
}, },
"author": "Abazov Andrey",
"license": "ISC",
"dependencies": { "dependencies": {
"bootstrap": "5.2.1", "react": "^18.2.0",
"@fortawesome/fontawesome-free": "6.2.0" "react-dom": "^18.2.0",
"react-router-dom": "^6.4.4",
"axios": "^1.1.3",
"bootstrap": "^5.2.2",
"@fortawesome/fontawesome-free": "^6.2.1"
}, },
"devDependencies": { "devDependencies": {
"http-server": "14.1.1" "@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"vite": "^3.2.3",
"@vitejs/plugin-react": "^2.2.0",
"npm-run-all": "^4.1.5"
} }
} }

View File

@ -1,70 +0,0 @@
function calcSecond(){
var num = document.getElementById("numberInput").value;
fetch("http://127.0.0.1:8080/second?num=" + num)
.then(function(response) {
if (response.status != 200){
return response.text().then(text => {throw new Error(text)});
}
return response.text();
})
.then((response) => {
document.getElementById("responseField").innerHTML = "Результат: " + response;
})
.catch(err => {document.getElementById("responseField").innerHTML = "Ошибка: " + err;})
}
function calcRoot(){
var num = document.getElementById("numberInput").value;
if (num < 0) {
document.getElementById("responseField").innerHTML = "Результат: введите НЕОТРИЦАТЕЛЬНОЕ число";
return;
}
fetch("http://127.0.0.1:8080/root?num=" + num)
.then((response) => {
if (response.status != 200){
return response.text().then(text => {throw new Error(text)});
}
return response.text();
})
.then((response) => {
console.log(response);
document.getElementById("responseField").innerHTML = "Результат: " + response;
})
.catch(err => {document.getElementById("responseField").innerHTML = "Ошибка: " + err;})
}
function calcFact(){
var num = document.getElementById("numberInput").value;
if (num < 0) {
document.getElementById("responseField").innerHTML = "Результат: введите НЕОТРИЦАТЕЛЬНОЕ число";
return;
}
fetch("http://127.0.0.1:8080/fact?num=" + num)
.then((response) => {
if (response.status != 200){
return response.text().then(text => {throw new Error(text)});
}
return response.text();
})
.then((response) => {
console.log(response);
document.getElementById("responseField").innerHTML = "Результат: " + response;
})
.catch(err => {document.getElementById("responseField").innerHTML = "Ошибка: " + err;})
}
function calcDigit(){
var num = document.getElementById("numberInput").value;
fetch("http://127.0.0.1:8080/digit?num=" + num)
.then((response) => {
if (response.status != 200){
return response.text().then(text => {throw new Error(text)});
}
return response.text();
})
.then((response) => {
console.log(response);
document.getElementById("responseField").innerHTML = "Результат: " + response;
})
.catch(err => {document.getElementById("responseField").innerHTML = "Ошибка: " + err;})
}

45
frontend/src/App.jsx Normal file
View File

@ -0,0 +1,45 @@
import { useRoutes, Outlet, BrowserRouter } from 'react-router-dom';
import Header from './components/common/Header';
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';
function Router(props) {
return useRoutes(props.rootRoute);
}
export default function App() {
const routes = [
{ index: true, element: <CustomerPage/> },
{ path: 'customer', element: <CustomerPage/>, label:'Покупатели'},
{ path: 'store', element: <StorePage/>, label: 'Магазины' },
{ path: 'product', element: <ProductPage/>, label: 'Товары' },
{ path: 'order', element: <OrderPage/>, label: 'Заказы'},
{ path: 'addToStore', element: <AddToStorePage/>, label: 'Доставка'}
];
const links = routes.filter(route => route.hasOwnProperty('label'));
const rootRoute = [
{ path: '/', element: render(links), children: routes }
];
function render(links) {
console.info('render links');
return (
<>
<Header links={links} />
<div className="container-fluid">
<Outlet />
</div>
</>
);
}
return (
<BrowserRouter>
<Router rootRoute={ rootRoute } />
</BrowserRouter>
);
}

View File

@ -0,0 +1,21 @@
import React from "react";
export default function Footer(){
return(
<div className="ml-0 mr-0 h-25 d-flex flex-column justify-content-end">
<footer className="footer d-flex container-fluid">
<div className="text-left text-nowrap ml-0 footerLeft fw-bold">
Контакты<br/>
+7(***)***-**-**<br/>
+7(***)***-**-**<br/>
</div>
<div className="text-bottom text-center mx-auto mt-auto mb-0 fw-bold footerBottom">
boxStore. inc, 2022
</div>
<div className="footerRight me-0 ms-auto my-0 d-flex flex-column">
<a href="https://www.apple.com/ru/app-store/"><img src="AppStore.png" width="110" height="55"/></a>
<img src="GooglePlay.png" width="110" height="55"/>
</div>
</footer>
</div>
);
}

View File

@ -0,0 +1,41 @@
import { NavLink } from "react-router-dom";
import React from 'react'
export default function Header(props){
return(
<header>
<div className="d-flex flex-row headerContainer">
<div className="d-flex flex-row ml-3 ms-3 mt-auto mb-auto align-items-center">
<a>
<img src="logo.png" alt="*" width="60" height="60" className="align-text-top"></img>
</a>
<div id="logoName">
<a href="customer">boxStore</a>
</div>
</div>
<nav className="navbar navbar-expand-md">
<div className="container-fluid" id="navigationMenu">
<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="navbar-collapse collapse justify-content-end" id="navbarNav">
<ul className="navbar-nav" id="headerNavigation">
{
props.links.map(route =>
<li key={route.path}
className="nav-item">
<NavLink className="nav-link navigationCaption" to={route.path}>
{route.label}
</NavLink>
</li>
)
}
</ul>
</div>
</div>
</nav>
</div>
</header>
);
}

View File

@ -0,0 +1,46 @@
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

@ -0,0 +1,73 @@
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-success 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

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

View File

@ -0,0 +1,29 @@
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 my-2 mx-4" 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

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

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,118 @@
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() {
let customer = {
lastName: props.data.lastName,
firstName: props.data.firstName,
middleName: props.data.middleName
}
if (!isEdit) {
DataService.create(props.url, customer).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, customer).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,121 @@
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() {
let ordered = {
productId: props.data.productId,
customerId: props.data.customerId,
quantity: props.data.quantity
}
if (!isEdit) {
DataService.create(props.url, ordered).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, ordered).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,116 @@
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() {
let product = {
productName: props.data.productName
}
if (!isEdit) {
DataService.create(props.url, product).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, product).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,116 @@
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() {
let store = {
storeName: props.data.storeName
}
if (!isEdit) {
DataService.create(props.url, store).then(() => loadItems());
} else {
DataService.update(props.getUrl + props.data.id, store).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,89 @@
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/'
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;
let product = {
id: productId
}
DataService.update(url + storeId + "/add", product);
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,53 @@
import Customer from "../../models/customer"
import CustomerTable from '../common/customerTable'
import { useState, useEffect} from "react";
export default function CustomerPage(){
const url = 'customer';
const getUrl = 'customer/';
const transformer = (data) => new Customer(data);
const catalogCustomerHeaders = [
{ name: 'lastName', label: 'Фамилия' },
{name: 'firstName', label: 'Имя'},
{name: 'middleName', label: 'Отчество'}
];
const [data, setData] = useState(new Customer());
function handleOnAdd() {
setData(new Customer());
}
function handleOnEdit(data) {
setData(new Customer(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">
<CustomerTable headers={catalogCustomerHeaders}
getAllUrl={url}
url={url}
getUrl={getUrl}
transformer={transformer}
data={data}
onAdd={handleOnAdd}
onEdit={handleOnEdit}>
<div className="col-md-4">
<label className="form-label" forhtml="lastName">Фамилия</label>
<input className="form-control" type="text" id="lastName" value={data.lastName} onChange={handleFormChange} required="required"/>
</div>
<div className="col-md-4">
<label className="form-label" forhtml="firstName">Имя</label>
<input className="form-control" type="text" value={data.firstName} onChange={handleFormChange} id="firstName" required="required"/>
</div>
<div className="col-md-4">
<label className="form-label" forhtml="middleName">Отчество</label>
<input className="form-control" type="text" id="middleName" value={data.middleName} onChange={handleFormChange} required="required"/>
</div>
</CustomerTable>
</article>
)
}

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>
)
}

9
frontend/src/main.jsx Normal file
View File

@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('app')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)

View File

@ -0,0 +1,8 @@
export default class Customer {
constructor(data) {
this.id = data?.id;
this.lastName = data?.lastName || '';
this.firstName = data?.firstName || '';
this.middleName = data?.middleName || '';
}
}

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;
}
}

View File

@ -0,0 +1,43 @@
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/api/';
static async readAll(url, transformer) {
const response = await axios.get(this.dataUrlPrefix + url);
console.log(response);
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, data);
return true;
}
static async update(url, data) {
const response = await axios.put(this.dataUrlPrefix + url, data);
return true;
}
static async delete(url) {
const response = await axios.delete(this.dataUrlPrefix + url);
return response.data.id;
}
}

278
frontend/src/styleSite.css Normal file
View File

@ -0,0 +1,278 @@
header{
background-color: #9094c1;
flex-direction: row;
}
header nav{
font-family: Segoe UI;
font-weight: bold;
font-size: 42px;
}
header nav a:hover{
text-decoration: underline;
}
.navigationCaption{
font-family: Segoe UI;
font-weight: bold;
font-size: 42px;
}
a{
color: inherit;
text-decoration: none;
}
td, th{
border: 1px solid black;
font-family: Segoe UI;
text-align: center;
font-size: 28px;
}
table tbody td a:hover{
text-decoration: underline;
color: inherit;
}
footer{
background-color: #707be5;
max-height: 110px;
}
.mainPage a:hover{
text-decoration: underline;
color: inherit;
}
.popularCaption{
font-family: Segoe UI;
font-size: 24px;
}
.discountsCaption{
font-family: Segoe UI;
font-size: 24px;
}
.item{
font-family: Segoe UI;
font-size: 18px;
}
.item img{
width: 200px;
height: 200px;
}
.tableMy table caption{
font-family: Segoe UI;
font-weight: bold;
font-size: 45px;
}
.tableMy table tbody td a:hover{
text-decoration: underline;
color: inherit !important;
}
.tableCart table caption{
font-family: Segoe UI;
font-weight: bold;
font-size: 32px;
}
.cartInfo{
margin-top: 320px;
margin-right: 400px;
font-family: Segoe UI;
font-size: 45px;
}
.buttonOrder{
background-color: #4d89d9;
}
.itemCaption{
font-family: Segoe UI;
font-size: 32px;
}
#itemPhoto{
margin-left: 50px;
}
.itemInfo{
font-family: Segoe UI;
font-size: 45px;
margin-left: 85px;
}
.itemInfo li{
list-style-type: none;
}
.buttonAdd{
font-family: Segoe UI;
font-size: 45px;
background-color: #4d89d9;
margin-left: 35px;
}
.companyName{
font-family: Segoe UI;
font-size: 45px;
}
.longText{
font-family: Segoe UI;
font-size: 25px;
}
#logoName{
font-family: Rockwell Condensed;
font-size: 64px;
font-weight: bold;
font-stretch: condensed;
text-decoration: none;
}
#logoName a:hover{
text-decoration: none;
color: inherit;
}
.footerLeft{
margin-bottom: 10px;
font-family: Segoe UI;
font-size: 16px;
}
.footerBottom{
font-family: Segoe UI;
font-size: 28px;
}
.hide{
display: none;
}
.active{
display: block;
}
.active img{
width: 200px;
height: 100px;
}
@media (max-width: 1080px){
header{
flex-direction: column;
}
header nav{
text-align: center;
}
.headerContainer{
flex-direction: column !important;
}
.itemContent{
flex-direction: column !important;
justify-content: center !important;
}
#itemPhoto{
margin-left: auto !important;
margin-right: auto ;
}
.itemInfo{
margin-bottom: 10px !important;
margin-left: 10px!important;
}
#cartContainer{
flex-direction: column !important;
}
.cartInfo{
margin-top: 0px !important;
margin-left: 5px;
margin-right: auto;
margin-bottom: 10px;
}
#tableCart{
width: auto;
}
.mainPage{
flex-direction: column !important;
justify-content: center;
}
.tablePopular{
margin-left: auto !important;
margin-right: auto !important;
}
.tableDiscounts{
margin-top: 30px;
margin-left: auto !important;
margin-right: auto !important;
}
}
@media (max-width: 767px){
.tableMy table thead th{
font-size: 12px !important;
}
.tableMy table tr td{
font-size: 12px !important;
}
.tableCart table thead th{
font-size: 18px !important;
}
.tableCart table tr td{
font-size: 18px !important;
}
.cartInfo{
font-size: 18px !important;
}
.itemInfo{
font-size: 18px !important;
}
.buttonAdd{
font-size: 18px !important
}
.footerLeft{
font-size: 12px !important;
}
.footerBottom{
font-size: 16px !important;
}
.footerRight img{
width: 55px;
height: 27px;
}
.mainPage img{
width: 100px !important;
height: 100px !important;
}
.popularCaption{
font-size: 18px !important;
}
.discountsCaption{
font-size: 18px !important;
}
#itemIcon{
width: 250px !important;
height: 250px !important;
}
}

View File

@ -1,6 +0,0 @@
#responseField{
font-family: Segoe UI;
font-size: 24px;
color: black;
text-decoration: none;
}

7
frontend/vite.config.js Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
})