Commit 2a31c172 authored by Vladimir Trubachoff's avatar Vladimir Trubachoff

Merge remote-tracking branch 'origin/fixes' into develop

parents fd540771 a1c8b5b1
package com.example.testj.domain;
import jakarta.persistence.*;
import jakarta.validation.constraints.Digits;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
@Entity
@Table(name = "goods")
public class Goods {
......@@ -18,13 +19,22 @@ public class Goods {
@Column(name = "name")
private String name;
@Digits
@NotEmpty
@Column(name = "price")
private Number price;
private double price;
public Goods() {
}
public Goods(Long id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
@OneToMany(mappedBy = "goods")
List<OrderLine> orderLines;
public Goods(Goods goods) {
this.name = goods.getName();
this.price = goods.getPrice();
}
public Long getId() {
return id;
......@@ -42,11 +52,11 @@ public class Goods {
this.name = name;
}
public Number getPrice() {
public Double getPrice() {
return price;
}
public void setPrice(Number price) {
public void setPrice(Double price) {
this.price = price;
}
}
package com.example.testj.domain;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.List;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
@Entity
@Table(name = "orders")
......@@ -26,7 +34,7 @@ public class Order {
@Column(name = "address")
private String address;
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
List<OrderLine> orderLines;
public Order() {
......@@ -38,10 +46,23 @@ public class Order {
this.date = date;
}
public void addOrderLine(OrderLine orderLine) {
orderLines.add(orderLine);
orderLine.setOrder(this);
}
public void removeOrderLine(OrderLine orderLine) {
orderLines.remove(orderLine);
}
public List<OrderLine> getOrderLines() {
return orderLines;
}
public void setOrderLines(List<OrderLine> orderLines) {
this.orderLines = orderLines;
}
public String getDate() {
return date;
}
......
package com.example.testj.domain;
import jakarta.persistence.*;
import org.springframework.format.annotation.NumberFormat;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Min;
@Entity
@Table(name = "order_line")
......@@ -13,20 +21,20 @@ public class OrderLine {
@Column(name = "id")
private Long id;
@JsonIgnore
@ManyToOne
@JoinColumn(name = "order_id", nullable = false)
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "goods_id", nullable = false)
@JoinColumn(name = "goods_id")
private Goods goods;
@Column(name = "count")
@NumberFormat
@Min(1)
private int count;
public Long getId() {
return id;
}
......@@ -46,4 +54,17 @@ public class OrderLine {
public Goods getGoods() {
return goods;
}
public void setGoods(Goods goods) {
this.goods = goods;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
......@@ -2,8 +2,9 @@ package com.example.testj.repository;
import com.example.testj.domain.Goods;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GoodsRepository extends CrudRepository<Goods, Long> {
public interface GoodsRepository extends PagingAndSortingRepository<Goods, Long>, CrudRepository<Goods, Long> {
}
\ No newline at end of file
package com.example.testj.service;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PagedModel;
import org.springframework.stereotype.Service;
import com.example.testj.domain.Goods;
import com.example.testj.exception.ResourceNotFoundException;
import com.example.testj.repository.GoodsRepository;
import com.example.testj.service.dto.GoodsDto;
import com.example.testj.utils.MappingUtils;
@Service
public class GoodsService {
private final GoodsRepository goodsRepository;
private final MappingUtils mappingUtils;
public GoodsService(GoodsRepository goodsRepository, MappingUtils mappingUtils) {
this.goodsRepository = goodsRepository;
this.mappingUtils = mappingUtils;
}
public PagedModel<GoodsDto> getAllGoods(Integer page, Integer size, String sortBy, String sortDir) {
return new PagedModel<>(goodsRepository
.findAll(PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDir), sortBy)))
.map(mappingUtils::toGoodsDto));
}
public GoodsDto getGoods(Long id) {
return goodsRepository.findById(id).map(mappingUtils::toGoodsDto).orElse(null);
}
public GoodsDto createOrUpdate(Goods goods) {
return mappingUtils.toGoodsDto(this.goodsRepository.save(goods));
}
public void deleteGoods(Long id) {
this.goodsRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
this.goodsRepository.deleteById(id);
}
}
package com.example.testj.service;
import org.springframework.stereotype.Service;
import com.example.testj.domain.OrderLine;
import com.example.testj.exception.ResourceNotFoundException;
import com.example.testj.repository.OrderLineRepository;
import com.example.testj.service.dto.GoodsDto;
import com.example.testj.service.dto.OrderDto;
import com.example.testj.service.dto.OrderLineDto;
import com.example.testj.service.dto.OrderLineRequestDto;
import com.example.testj.utils.MappingUtils;
@Service
public class OrderLineService {
private final MappingUtils mappingUtils;
private final OrderLineRepository orderLineRepository;
private final OrderService orderService;
private final GoodsService goodsService;
public OrderLineService(OrderLineRepository orderLineRepository, MappingUtils mappingUtils,
OrderService orderService, GoodsService goodsService) {
this.orderLineRepository = orderLineRepository;
this.mappingUtils = mappingUtils;
this.orderService = orderService;
this.goodsService = goodsService;
}
public OrderLineDto createOrderLine(OrderLineRequestDto orderLineRequest) {
OrderDto order = orderService.getOrder(orderLineRequest.getOrderId());
GoodsDto goods = goodsService.getGoods(orderLineRequest.getGoodsId());
OrderLine orderLine = new OrderLine();
orderLine.setOrder(mappingUtils.toOrder(order));
orderLine.setGoods(mappingUtils.toGoods(goods));
orderLine.setCount(orderLineRequest.getCount());
return mappingUtils.toOrderLineDto(orderLineRepository.save(orderLine));
}
public OrderLineDto updateOrderLine(OrderLine orderLine, Long id) {
OrderLine updatedOrderLine = orderLineRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
updatedOrderLine.setCount(orderLine.getCount());
return mappingUtils.toOrderLineDto(orderLineRepository.save(updatedOrderLine));
}
public OrderLineDto getOrderLine(Long id) throws ResourceNotFoundException {
return mappingUtils
.toOrderLineDto(orderLineRepository.findById(id).orElseThrow(ResourceNotFoundException::new));
}
public void deleteOrderLine(Long id) {
orderLineRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
orderLineRepository.deleteById(id);
}
}
package com.example.testj.service;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PagedModel;
import org.springframework.stereotype.Service;
import com.example.testj.domain.Order;
import com.example.testj.dto.OrderDTO;
import com.example.testj.exception.ResourceNotFoundException;
import com.example.testj.repository.OrderRepository;
import com.example.testj.service.dto.OrderDto;
import com.example.testj.utils.MappingUtils;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PagedModel;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final MappingUtils mappingUtils;
public OrderService(OrderRepository orderRepository) {
public OrderService(OrderRepository orderRepository, MappingUtils mappingUtils) {
this.orderRepository = orderRepository;
this.mappingUtils = mappingUtils;
}
public PagedModel<Order> getOrders(Integer page, Integer size, String sortBy, String sortDir) {
return new PagedModel<Order>(orderRepository.findAll(PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDir), sortBy))));
public PagedModel<OrderDto> getOrders(Integer page, Integer size, String sortBy, String sortDir) {
return new PagedModel<>(orderRepository
.findAll(PageRequest.of(page, size, Sort.by(Sort.Direction.fromString(sortDir), sortBy)))
.map(mappingUtils::toOrderDto));
}
public Order getOrder(Long id) throws ResourceNotFoundException {
return orderRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
public OrderDto getOrder(Long id) throws ResourceNotFoundException {
return mappingUtils.toOrderDto(orderRepository.findById(id).orElseThrow(ResourceNotFoundException::new));
}
public Order saveOrUpdateOrder(Order order) {
return orderRepository.save(order);
public void deleteOrder(Long id) throws ResourceNotFoundException {
orderRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
orderRepository.deleteById(id);
}
public Order patchOrder(Long id, JsonNode patch) throws JsonMappingException {
Order orderData = orderRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
Order patchedOrder = new ObjectMapper().updateValue(orderData, patch);
return orderRepository.save(patchedOrder);
public OrderDto createOrUpdateOrder(Order order) {
return mappingUtils.toOrderDto(orderRepository.save(order));
}
public void deleteOrder(Long id) throws ResourceNotFoundException {
orderRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
orderRepository.deleteById(id);
public OrderDto patchOrder(Long id, JsonNode patch) throws JsonMappingException {
Order orderData = orderRepository.findById(id).orElseThrow(ResourceNotFoundException::new);
Order patchedOrder = new ObjectMapper().updateValue(orderData, patch);
return mappingUtils.toOrderDto(orderRepository.save(patchedOrder));
}
}
package com.example.testj.dto;
package com.example.testj.service.dto;
import java.io.Serializable;
import com.example.testj.domain.Goods;
/**
* DTO for {@link com.example.testj.domain.Goods}
*/
public class GoodsDto implements Serializable {
private final Long id;
private final String name;
private final Number price;
private Long id;
private String name;
private double price;
public GoodsDto(Long id, String name, Number price) {
this.id = id;
this.name = name;
this.price = price;
public GoodsDto() {
}
public GoodsDto(Goods goods) {
this.id = goods.getId();
this.name = goods.getName();
this.price = goods.getPrice();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public Number getPrice() {
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" +
"id = " + id + ", " +
"name = " + name + ", " +
"price = " + price + ")";
public void setPrice(double price) {
this.price = price;
}
}
\ No newline at end of file
package com.example.testj.dto;
package com.example.testj.service.dto;
import java.io.Serializable;
import java.util.List;
import com.example.testj.domain.Order;
import com.example.testj.domain.OrderLine;
import java.io.Serializable;
public class OrderDto implements Serializable {
private Long id;
private String client;
private String date;
private String address;
public class OrderDTO implements Serializable {
public OrderDTO(Order order) {
private List<OrderLine> orderLines;
public OrderDto() {
}
public OrderDto(Order order) {
this.id = order.getId();
this.client = order.getClient();
this.address = order.getAddress();
this.date = order.getDate();
this.orderLines = order.getOrderLines();
}
private Long id;
private String client;
private String date;
private String address;
public String getDate() {
return date;
}
......@@ -45,7 +53,11 @@ public class OrderDTO implements Serializable {
return id;
}
public void setId(Long id) {
this.id = id;
public List<OrderLine> getOrderLines() {
return orderLines;
}
public void setOrderLines(List<OrderLine> orderLines) {
this.orderLines = orderLines;
}
}
package com.example.testj.service.dto;
import java.io.Serializable;
import com.example.testj.domain.Goods;
import com.example.testj.domain.Order;
import com.example.testj.domain.OrderLine;
public class OrderLineDto implements Serializable {
private Long id;
private Goods goods;
private Order order;
private int count;
public OrderLineDto() {
}
public OrderLineDto(OrderLine orderLine) {
this.id = orderLine.getId();
this.count = orderLine.getCount();
this.goods = orderLine.getGoods();
this.order = orderLine.getOrder();
}
public Goods getGoods() {
return this.goods;
}
public void setGoods(Goods goods) {
this.goods = goods;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Long getId() {
return id;
}
public Order getOrder() {
return order;
}
}
\ No newline at end of file
package com.example.testj.service.dto;
public class OrderLineRequestDto {
private Long orderId;
private Long goodsId;
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Long getOrderId() {
return orderId;
}
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
public Long getGoodsId() {
return goodsId;
}
public void setGoodsId(Long goodsId) {
this.goodsId = goodsId;
}
}
package com.example.testj.utils;
import org.springframework.stereotype.Service;
import com.example.testj.domain.Goods;
import com.example.testj.domain.Order;
import com.example.testj.domain.OrderLine;
import com.example.testj.service.dto.GoodsDto;
import com.example.testj.service.dto.OrderDto;
import com.example.testj.service.dto.OrderLineDto;
@Service
public class MappingUtils {
public OrderDto toOrderDto(Order order) {
return new OrderDto(order);
}
public Order toOrder(OrderDto orderDto) {
Order order = new Order();
order.setId(orderDto.getId());
order.setClient(orderDto.getClient());
order.setDate(orderDto.getDate());
order.setAddress(orderDto.getAddress());
order.setOrderLines(orderDto.getOrderLines());
return order;
}
public OrderLineDto toOrderLineDto(OrderLine orderLine) {
return new OrderLineDto(orderLine);
}
public OrderLine toOrderLine(OrderLineDto orderLineDto) {
OrderLine orderLine = new OrderLine();
orderLine.setGoods(orderLineDto.getGoods());
orderLine.setOrder(orderLineDto.getOrder());
orderLine.setCount(orderLineDto.getCount());
return orderLine;
}
public GoodsDto toGoodsDto(Goods goods) {
return new GoodsDto(goods);
}
public Goods toGoods(GoodsDto goodsDto) {
Goods goods = new Goods();
goods.setId(goodsDto.getId());
goods.setName(goodsDto.getName());
goods.setPrice(goodsDto.getPrice());
return goods;
}
}
package com.example.testj.web.rest;
import com.example.testj.domain.Goods;
import com.example.testj.repository.GoodsRepository;
import org.springframework.data.web.PagedModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import com.example.testj.exception.ResourceNotFoundException;
import com.example.testj.service.GoodsService;
import com.example.testj.service.dto.GoodsDto;
import com.example.testj.utils.MappingUtils;
@RestController
@RequestMapping("/api/goods")
@CrossOrigin(origins = "http://localhost:4200")
public class GoodsController {
private final GoodsRepository goodsRepository;
private final GoodsService goodsService;
private final MappingUtils mappingUtils;
public GoodsController(GoodsRepository goodsRepository) {
this.goodsRepository = goodsRepository;
public GoodsController(GoodsService goodsService, MappingUtils mappingUtils) {
this.goodsService = goodsService;
this.mappingUtils = mappingUtils;
}
@GetMapping("/goods")
public List<Goods> goods() {
return (List<Goods>) goodsRepository.findAll();
@GetMapping
public PagedModel<GoodsDto> goods(
@RequestHeader(name = "X-Page-Current", defaultValue = "0") Integer page,
@RequestHeader(name = "X-Page-Size", defaultValue = "10") Integer size,
@RequestHeader(name = "X-Sort-By", defaultValue = "id") String sortBy,
@RequestHeader(name = "X-Sort-Direction", defaultValue = "ASC") String sortDir) {
return goodsService.getAllGoods(page, size, sortBy, sortDir);
}
@GetMapping("/{id}")
public GoodsDto goods(@PathVariable Long id) {
return goodsService.getGoods(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public GoodsDto createGoods(@RequestBody GoodsDto goods) {
return this.goodsService.createOrUpdate(mappingUtils.toGoods(goods));
}
@PutMapping("/{id}")
public GoodsDto updateGoods(@PathVariable Long id, @RequestBody GoodsDto goods) {
GoodsDto updatedGoods = goodsService.getGoods(id);
updatedGoods.setName(goods.getName());
updatedGoods.setPrice(goods.getPrice());
return this.goodsService.createOrUpdate(mappingUtils.toGoods(updatedGoods));
}
@DeleteMapping("/{id}")
public ResponseEntity<HttpStatus> deleteGoods(@PathVariable Long id) {
try {
goodsService.deleteGoods(id);
return new ResponseEntity<>(HttpStatus.OK);
} catch (ResourceNotFoundException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
package com.example.testj.web.rest;
import org.springframework.data.web.PagedModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.example.testj.domain.Order;
import com.example.testj.domain.RestErrorInfo;
import com.example.testj.dto.OrderDTO;
import com.example.testj.exception.ResourceNotFoundException;
import com.example.testj.repository.OrderRepository;
import com.example.testj.service.OrderService;
import com.example.testj.service.dto.OrderDto;
import com.example.testj.utils.MappingUtils;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import jakarta.validation.Valid;
import org.springframework.data.web.PagedModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api")
@RequestMapping("/api/orders")
@CrossOrigin(origins = "http://localhost:4200")
public class OrderController {
private final OrderRepository orderRepository;
private final OrderService orderService;
private final MappingUtils mappingUtils;
public OrderController(OrderRepository orderRepository, OrderService orderService) {
this.orderRepository = orderRepository;
public OrderController(OrderService orderService, MappingUtils mappingUtils) {
this.orderService = orderService;
this.mappingUtils = mappingUtils;
}
@GetMapping("/orders")
public PagedModel<Order> getOrders(@RequestHeader(name = "X-Current-Page", defaultValue = "0") Integer page,
@RequestHeader(name = "X-Page-Size", defaultValue = "10") Integer size,
@RequestHeader(name = "X-Sort-By", defaultValue = "id") String sortBy,
@RequestHeader(name = "X-Sort-Direction", defaultValue = "ASC") String sortDir) {
@GetMapping
public PagedModel<OrderDto> getOrders(
@RequestHeader(name = "X-Page-Current", defaultValue = "0") Integer page,
@RequestHeader(name = "X-Page-Size", defaultValue = "10") Integer size,
@RequestHeader(name = "X-Sort-By", defaultValue = "id") String sortBy,
@RequestHeader(name = "X-Sort-Direction", defaultValue = "ASC") String sortDir) {
return orderService.getOrders(page, size, sortBy, sortDir);
}
@GetMapping("/orders/{id}")
public Order getOrder(@PathVariable Long id) {
return this.orderService.getOrder(id);
@GetMapping("/{id}")
public OrderDto getOrder(@PathVariable Long id) {
return orderService.getOrder(id);
}
@PostMapping("/orders")
public Order createOrder(@Valid @RequestBody Order order) {
return orderService.saveOrUpdateOrder(order);
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public OrderDto createOrder(@Valid @RequestBody Order order) {
return orderService.createOrUpdateOrder(order);
}
@PutMapping("/orders/{id}")
public Order updateOrder(@PathVariable Long id, @RequestBody Order order) {
Order updatedOrder = orderService.getOrder(id);
updatedOrder.setClient(order.getClient());
updatedOrder.setAddress(order.getAddress());
updatedOrder.setDate(order.getDate());
return orderService.saveOrUpdateOrder(updatedOrder);
@PutMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public OrderDto updateOrder(@PathVariable Long id, @RequestBody OrderDto order) {
OrderDto updatedOrder = orderService.getOrder(id);
updatedOrder.setClient(order.getClient());
updatedOrder.setAddress(order.getAddress());
updatedOrder.setDate(order.getDate());
return orderService.createOrUpdateOrder(mappingUtils.toOrder(updatedOrder));
}
@PatchMapping("/orders/{id}")
public Order patchOrder(@PathVariable Long id, @RequestBody JsonNode patch) throws JsonMappingException {
@PatchMapping("/{id}")
public OrderDto patchOrder(@PathVariable Long id, @RequestBody JsonNode patch) throws JsonMappingException {
return orderService.patchOrder(id, patch);
}
@DeleteMapping("/orders/{id}")
@DeleteMapping("/{id}")
public ResponseEntity<HttpStatus> deleteOrder(@PathVariable Long id) throws ResourceNotFoundException {
try {
orderService.deleteOrder(id);
......@@ -68,6 +83,5 @@ public class OrderController {
} catch (ResourceNotFoundException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
package com.example.testj.web.rest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.testj.exception.ResourceNotFoundException;
import com.example.testj.service.OrderLineService;
import com.example.testj.service.dto.OrderLineDto;
import com.example.testj.service.dto.OrderLineRequestDto;
import com.example.testj.utils.MappingUtils;
import jakarta.validation.Valid;
@RestController
@RequestMapping("/api/order-line")
@CrossOrigin(origins = "http://localhost:4200")
public class OrderLineController {
private final OrderLineService orderLineService;
private final MappingUtils mappingUtils;
public OrderLineController(OrderLineService orderLineService, MappingUtils mappingUtils) {
this.orderLineService = orderLineService;
this.mappingUtils = mappingUtils;
}
@GetMapping("/{id}")
public OrderLineDto get(@PathVariable Long id) {
return orderLineService.getOrderLine(id);
}
@PostMapping
public OrderLineDto create(@Valid @RequestBody OrderLineRequestDto orderLineRequest) {
return orderLineService.createOrderLine(orderLineRequest);
}
@PutMapping("/{id}")
public OrderLineDto updateOrder(@PathVariable Long id, @Valid @RequestBody OrderLineDto orderLine) {
return orderLineService.updateOrderLine(mappingUtils.toOrderLine(orderLine), id);
}
@DeleteMapping("/{id}")
public ResponseEntity<HttpStatus> delete(@PathVariable Long id) throws ResourceNotFoundException {
try {
orderLineService.deleteOrderLine(id);
return new ResponseEntity<>(HttpStatus.OK);
} catch (ResourceNotFoundException e) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
......@@ -2,7 +2,7 @@ spring.application.name=testj
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.defer-datasource-initialization=true
spring.h2.console.enabled=true
......
This diff is collapsed.
{
"overrides": [
{
"files": "*.html",
"options": {
"parser": "angular"
}
}
]
}
# Testj
# Ng17
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.0.2.
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.8.
## Development server
......@@ -24,4 +24,4 @@ Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To u
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
......@@ -3,18 +3,12 @@
"version": 1,
"newProjectRoot": "projects",
"projects": {
"testj": {
"ng17": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss",
"standalone": false
},
"@schematics/angular:directive": {
"standalone": false
},
"@schematics/angular:pipe": {
"standalone": false
}
},
"root": "",
......@@ -24,21 +18,15 @@
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/testj",
"outputPath": "dist/ng17",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"assets": ["src/favicon.ico", "src/assets"],
"styles": [
"node_modules/bootstrap-icons/font/bootstrap-icons.scss",
"src/styles.scss"
],
"scripts": []
......@@ -48,13 +36,13 @@
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kB",
"maximumError": "4kB"
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
......@@ -77,35 +65,28 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "testj:build:production"
"buildTarget": "ng17:build:production"
},
"development": {
"buildTarget": "testj:build:development"
"buildTarget": "ng17:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "ng17:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.scss"
],
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.scss"],
"scripts": []
}
}
......
This diff is collapsed.
{
"name": "testj",
"name": "ng17",
"version": "0.0.0",
"scripts": {
"ng": "ng",
......@@ -10,22 +10,27 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^18.0.0",
"@angular/common": "^18.0.0",
"@angular/compiler": "^18.0.0",
"@angular/core": "^18.0.0",
"@angular/forms": "^18.0.0",
"@angular/platform-browser": "^18.0.0",
"@angular/platform-browser-dynamic": "^18.0.0",
"@angular/router": "^18.0.0",
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.2",
"bootstrap-icons": "^1.11.3",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.0.2",
"@angular/cli": "^18.0.2",
"@angular/compiler-cli": "^18.0.0",
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.0",
"@angular/localize": "^17.3.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
......@@ -33,6 +38,7 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"prettier": "^3.3.2",
"typescript": "~5.4.2"
}
}
.main {
}
.content {
width: 100%;
padding: 20px;
}
......@@ -3,17 +3,25 @@ import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {provideHttpClient} from "@angular/common/http";
import { provideHttpClient } from '@angular/common/http';
import {
NgbDateAdapter,
NgbDateParserFormatter,
NgbModule,
} from '@ng-bootstrap/ng-bootstrap';
import {
CustomDateAdapter,
CustomDateParserFormatter,
} from './core/adapter/custom-date.adapter';
@NgModule({
declarations: [
AppComponent
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule, NgbModule],
providers: [
provideHttpClient(),
{ provide: NgbDateAdapter, useClass: CustomDateAdapter },
{ provide: NgbDateParserFormatter, useClass: CustomDateParserFormatter },
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [provideHttpClient()],
bootstrap: [AppComponent]
bootstrap: [AppComponent],
})
export class AppModule { }
export class AppModule {}
import { Injectable } from '@angular/core';
import {
NgbDateParserFormatter,
NgbDateStruct,
} from '@ng-bootstrap/ng-bootstrap';
/**
* This Service handles how the date is represented in scripts i.e. ngModel.
*/
@Injectable()
export class CustomDateAdapter {
readonly DELIMITER = '/';
fromModel(value: string | null): NgbDateStruct | null {
if (value) {
const date = value.split(this.DELIMITER);
return {
day: parseInt(date[0], 10),
month: parseInt(date[1], 10),
year: parseInt(date[2], 10),
};
}
return null;
}
toModel(date: NgbDateStruct | null): string | null {
return date
? date.day + this.DELIMITER + date.month + this.DELIMITER + date.year
: null;
}
}
/**
* This Service handles how the date is rendered and parsed from keyboard i.e. in the bound input field.
*/
@Injectable()
export class CustomDateParserFormatter extends NgbDateParserFormatter {
readonly DELIMITER = '/';
parse(value: string): NgbDateStruct | null {
if (value) {
const date = value.split(this.DELIMITER);
return {
day: parseInt(date[0], 10),
month: parseInt(date[1], 10),
year: parseInt(date[2], 10),
};
}
return null;
}
format(date: NgbDateStruct | null): string {
return date
? date.day + this.DELIMITER + date.month + this.DELIMITER + date.year
: '';
}
}
export interface User {
export interface Goods {
id: number;
name: string;
price: number;
......
export interface OrderLine {
id: number;
order_id: number;
goods_id: number;
count: number;
}
import { Goods } from './goods.model';
export interface Order {
id: number;
address: string;
client: string;
date: string;
orderLines: OrderLine[];
}
export interface OrderLine {
id: number;
goods: Goods;
count: number;
}
export interface OrderLineReq {
id?: number;
orderId: number;
goodsId: number;
count: number;
}
export interface PagedResponseModel<T> {
content: T[];
page: PageModel;
}
export interface PageModel {
number: number;
size: number;
totalElements: number;
totalPages: number;
}
export interface ControlsValidations {
[key: string]: ValidationMessage[];
}
export interface ValidationMessage {
type: string;
message: string;
params?: object;
}
import { TestBed } from '@angular/core/testing';
import { GoodsService } from './goods.service';
describe('GoodsService', () => {
let service: GoodsService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(GoodsService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { environment } from '../../../environments/environment';
import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http';
import { catchError, EMPTY, Observable } from 'rxjs';
import { PagedResponseModel } from '../model/paged-response.model';
import { Goods } from '../model/goods.model';
@Injectable({
providedIn: 'root',
})
export class GoodsService {
private readonly apiUrl = `${environment.API_ROOT}${environment.API_PREFIX}${environment.GOODS_URL}`;
private page = 0;
private limit = 10;
constructor(private http: HttpClient) {}
get(
page = this.page,
limit = this.limit
): Observable<PagedResponseModel<Goods>> {
const url = `${this.apiUrl}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
'X-Page-Current': String(page),
'X-Page-Size': String(limit),
});
return this.http.get<PagedResponseModel<Goods>>(url, { headers });
}
put(goods: Goods): Observable<Goods> {
const url = `${this.apiUrl}/${goods.id}`;
return this.http.put<Goods>(url, goods);
}
post(goods: Goods): Observable<Goods> {
const url = this.apiUrl;
return this.http.post<Goods>(url, goods);
}
delete(id: number) {
const url = `${this.apiUrl}/${id}`;
return this.http.delete(url).pipe(
catchError((err: HttpErrorResponse) => {
console.dir(err);
return EMPTY;
})
);
}
}
import { TestBed } from '@angular/core/testing';
import { OrderLineService } from './order-line.service';
describe('OrderLineService', () => {
let service: OrderLineService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(OrderLineService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError, EMPTY } from 'rxjs';
import { environment } from '../../../environments/environment';
import { OrderLine, OrderLineReq } from '../model/order.model';
@Injectable({
providedIn: 'root',
})
export class OrderLineService {
private readonly apiUrl = `${environment.API_ROOT}${environment.API_PREFIX}${environment.ORDER_LINE_URL}`;
constructor(private http: HttpClient) {}
put(ol: OrderLineReq) {
const url = `${this.apiUrl}/${ol.id}`;
return this.http.put<OrderLine>(url, ol);
}
post(ol: OrderLineReq) {
const url = this.apiUrl;
return this.http.post<OrderLine>(url, ol);
}
delete(id: number) {
const url = `${this.apiUrl}/${id}`;
return this.http.delete(url).pipe(
catchError((err: HttpErrorResponse) => {
console.dir(err);
return EMPTY;
})
);
}
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http';
import { catchError, EMPTY, Observable } from 'rxjs';
import { environment } from '../../../environments/environment';
import { Order } from '../model/order.model';
import { PagedResponseModel } from '../model/paged-response.model';
@Injectable({
providedIn: 'root',
})
export class OrderService {
private readonly apiUrl = `${environment.API_ROOT}${environment.API_PREFIX}${environment.ORDER_URL}`;
private page = 1;
private limit = 10;
private page = 0;
private size = 10;
constructor(private http: HttpClient) {}
getOrders(): Observable<Order[]> {
getOrders(
page = this.page,
size = this.size
): Observable<PagedResponseModel<Order>> {
const url = `${this.apiUrl}`;
const headers = new HttpHeaders({
'Content-Type': 'application/json',
Page: String(this.page),
Limit: String(this.limit),
'X-Page-Current': String(page),
'X-Page-Size': String(size),
});
return this.http.get<Order[]>(url, { headers });
return this.http.get<PagedResponseModel<Order>>(url, { headers });
}
getOrder(id: number): Observable<Order> {
const url = `${this.apiUrl}/${id}`;
return this.http.get<Order>(url);
}
putOrder(order: Order) {
const url = `${this.apiUrl}/${order.id}`;
return this.http.put<Order>(url, order);
}
postOrder(order: Order) {
const url = this.apiUrl;
return this.http.post<Order>(url, order);
}
deleteOrder(id: number) {
const url = `${this.apiUrl}/${id}`;
return this.http.delete(url).pipe(
catchError((err: HttpErrorResponse) => {
console.dir(err);
return EMPTY;
})
);
}
}
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Product</h4>
<button
type="button"
class="btn-close"
aria-label="Close"
(click)="modal?.close()"
></button>
</div>
<div class="modal-body">
<app-goods-form
[product]="product"
(isUpdate)="goodsUpdate()"
></app-goods-form>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GoodsEditModalComponent } from './goods-edit-modal.component';
describe('GoodsEditModalComponent', () => {
let component: GoodsEditModalComponent;
let fixture: ComponentFixture<GoodsEditModalComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GoodsEditModalComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GoodsEditModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { takeUntil } from 'rxjs';
import { environment } from '../../../../../environments/environment';
import { AbstractBasicComponent } from '../../../../core/abstract/abstract-component';
import { Goods } from '../../../../core/model/goods.model';
const DEV = !environment.production;
@Component({
selector: 'app-goods-edit-modal',
templateUrl: './goods-edit-modal.component.html',
styleUrl: './goods-edit-modal.component.scss',
})
export class GoodsEditModalComponent extends AbstractBasicComponent {
@Input() product!: Goods;
@Output() isUpdate = new EventEmitter<boolean>();
modal: NgbModalRef | undefined;
constructor(modalService: NgbModal) {
super();
modalService.activeInstances
.pipe(takeUntil(this.destroy$))
.subscribe((mls) => {
this.modal = mls[mls.length - 1];
});
}
goodsUpdate() {
this.modal?.close();
this.isUpdate.emit(true);
}
}
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@for (product of goods; track product.id){
<tr>
<th scope="row">{{ product.id }}</th>
<td>{{ product.name }}</td>
<td>{{ product.price | currency }}</td>
<td>
<button
type="button"
class="btn btn-sm btn-outline-secondary me-2"
(click)="openEdit(product)"
>
Edit
</button>
<button
type="button"
class="btn btn-sm btn-outline-danger me-2"
(click)="deleteProduct(product.id)"
>
Delete
</button>
</td>
</tr>
}
</tbody>
</table>
@if (page) {
<ngb-pagination
[page]="page.number + 1"
(pageChange)="pageNumberChange($event - 1)"
[pageSize]="page.size"
[collectionSize]="page.totalElements"
></ngb-pagination>
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GoodsListComponent } from './goods-list.component';
describe('GoodsListComponent', () => {
let component: GoodsListComponent;
let fixture: ComponentFixture<GoodsListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [GoodsListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GoodsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Goods } from '../../../../core/model/goods.model';
import { PageModel } from '../../../../core/model/paged-response.model';
@Component({
selector: 'app-goods-list',
templateUrl: './goods-list.component.html',
styleUrl: './goods-list.component.scss',
})
export class GoodsListComponent {
@Input() goods!: Goods[];
@Input() page!: PageModel;
@Output() pageNumber = new EventEmitter<number>();
@Output() openModal = new EventEmitter<Goods>();
@Output() delete = new EventEmitter<number>();
pageNumberChange(page: number) {
this.pageNumber.emit(page);
}
openEdit(product: Goods) {
this.openModal.emit(product);
}
deleteProduct(id: number) {
this.delete.emit(id);
}
}
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Order</h4>
<button
type="button"
class="btn-close"
aria-label="Close"
(click)="modal.close()"
></button>
</div>
<div class="modal-body">
<app-order-form
[order]="order"
(orderChange)="orderChanged($event)"
></app-order-form>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderEditModalComponent } from './order-edit-modal.component';
describe('OrderEditModalComponent', () => {
let component: OrderEditModalComponent;
let fixture: ComponentFixture<OrderEditModalComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OrderEditModalComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OrderEditModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { takeUntil } from 'rxjs';
import { AbstractBasicComponent } from '../../../../core/abstract/abstract-component';
import { Order } from '../../../../core/model/order.model';
@Component({
selector: 'app-order-edit-modal',
templateUrl: './order-edit-modal.component.html',
styleUrl: './order-edit-modal.component.scss',
})
export class OrderEditModalComponent extends AbstractBasicComponent {
@Input() order!: Order;
@Output() orderChange = new EventEmitter<Order>();
modal!: NgbModalRef;
constructor(modalService: NgbModal) {
super();
modalService.activeInstances
.pipe(takeUntil(this.destroy$))
.subscribe((mls) => {
this.modal = mls[mls.length - 1];
});
}
orderChanged(order: Order) {
this.orderChange.emit(order);
this.modal.close();
}
}
<ul>
<li *ngFor="let order of orders">
<dl>
<dt>Client</dt>
<dd>
{{ order.client }}
</dd>
<dt>Date</dt>
<dd>{{ order.date | date }}</dd>
</dl>
</li>
</ul>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Client</th>
<th scope="col">Address</th>
<th scope="col">Date</th>
<th scope="col">Products</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@for (order of orders; track order.id){
<tr>
<th scope="row">{{ order.id }}</th>
<td>{{ order.client }}</td>
<td>{{ order.address }}</td>
<td>{{ order.date | date }}</td>
<td>{{ order.orderLines.length }}</td>
<td>
<a
class="btn btn-sm btn-outline-secondary me-2"
role="button"
[routerLink]="['order', order.id]"
>
Details
</a>
<button
type="button"
class="btn btn-sm btn-outline-secondary me-2"
(click)="openOrder(order)"
>
Edit
</button>
<button
type="button"
class="btn btn-sm btn-outline-danger me-2"
(click)="deleteOrder(order.id)"
>
Delete
</button>
</td>
</tr>
}
</tbody>
</table>
@if (page) {
<ngb-pagination
[page]="page.number + 1"
(pageChange)="pageNumberChange($event - 1)"
[pageSize]="page.size"
[collectionSize]="page.totalElements"
></ngb-pagination>
}
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {AsyncPipe, DatePipe, NgForOf} from "@angular/common";
import {Observable} from "rxjs";
import {Order} from "../../../../core/model/order.model";
import {AbstractBasicComponent} from "../../../../core/abstract/abstract-component";
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Order } from '../../../../core/model/order.model';
import { PageModel } from '../../../../core/model/paged-response.model';
@Component({
selector: 'app-orders-list',
......@@ -11,8 +9,21 @@ import {AbstractBasicComponent} from "../../../../core/abstract/abstract-compone
})
export class OrdersListComponent {
@Input() orders!: Order[];
@Input() page!: PageModel;
@Output() pageNumber = new EventEmitter<number>();
constructor(
){
@Output() openModal = new EventEmitter<Order>();
@Output() delete = new EventEmitter<number>();
pageNumberChange(page: number) {
this.pageNumber.emit(page);
}
openOrder(order: Order) {
this.openModal.emit(order);
}
deleteOrder(id: number) {
this.delete.emit(id);
}
}
<app-orders-list [orders]="(orders$ | async)!"></app-orders-list>
<ul ngbNav #nav="ngbNav" class="nav-tabs" [destroyOnHide]="false">
<li ngbNavItem>
<button ngbNavLink>Orders</button>
<ng-template ngbNavContent>
<div class="py-2 float-end">
<a
role="button"
[routerLink]="['order']"
class="btn btn-outline-secondary me-2"
>
New order
</a>
</div>
<ng-container *ngIf="ordersSubj$ | async as orders; else loading">
<app-orders-list
[orders]="orders.content"
[page]="orders.page"
(pageNumber)="ordersPageNumber($event)"
(openModal)="openOrderModal($event)"
(delete)="deleteOrder($event)"
></app-orders-list>
</ng-container>
<ng-template #loading> loading... </ng-template>
</ng-template>
</li>
<li ngbNavItem>
<button ngbNavLink>Products</button>
<ng-template ngbNavContent>
<div class="py-2 float-end">
<button
class="btn btn-outline-secondary me-2"
(click)="openNewProductModal()"
>
New product
</button>
</div>
<ng-container *ngIf="goodsSubj$ | async as goods; else loading">
<app-goods-list
[goods]="goods.content"
[page]="goods.page"
(pageNumber)="goodsPageNumber($event)"
(openModal)="openGoodsModal($event)"
(delete)="deleteGoods($event)"
></app-goods-list>
</ng-container>
<ng-template #loading> loading... </ng-template>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
import {Component, OnInit} from '@angular/core';
import {Observable} from "rxjs";
import {Order} from "../../core/model/order.model";
import {OrderService} from "../../core/service/order.service";
import {AbstractBasicComponent} from "../../core/abstract/abstract-component";
import { Component, OnInit } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subject, concatMap, filter, take } from 'rxjs';
import { AbstractBasicComponent } from '../../core/abstract/abstract-component';
import { Goods } from '../../core/model/goods.model';
import { Order } from '../../core/model/order.model';
import {
PageModel,
PagedResponseModel,
} from '../../core/model/paged-response.model';
import { GoodsService } from '../../core/service/goods.service';
import { OrderService } from '../../core/service/order.service';
import { GoodsEditModalComponent } from './components/goods-edit-modal/goods-edit-modal.component';
import { OrderEditModalComponent } from './components/order-edit-modal/order-edit-modal.component';
@Component({
selector: 'app-index-page',
templateUrl: './index-page.component.html',
styleUrl: './index-page.component.scss'
styleUrl: './index-page.component.scss',
})
export class IndexPageComponent extends AbstractBasicComponent implements OnInit {
export class IndexPageComponent
extends AbstractBasicComponent
implements OnInit
{
protected ordersSubj$ = new Subject<PagedResponseModel<Order>>();
protected goodsSubj$ = new Subject<PagedResponseModel<Goods>>();
protected orders$!: Observable<Order[]>;
goodsPage!: PageModel;
orderPage!: PageModel;
constructor(private orderService: OrderService) {
constructor(
private orderService: OrderService,
private goodsService: GoodsService,
private modalService: NgbModal,
) {
super();
}
ngOnInit() {
this.orders$ = this.orderService.getOrders();
this.orderService.getOrders().subscribe((resp) => {
this.orderPage = resp.page;
this.ordersSubj$.next(resp);
});
this.goodsService.get().subscribe((resp) => {
this.goodsPage = resp.page;
this.goodsSubj$.next(resp);
});
}
ordersPageNumber(page: number) {
this.orderService.getOrders(page).subscribe((resp) => {
this.orderPage = resp.page;
this.ordersSubj$.next(resp);
});
}
goodsPageNumber(page: number) {
this.goodsService.get(page).subscribe((resp) => {
this.goodsPage = resp.page;
this.goodsSubj$.next(resp);
});
}
deleteOrder(id: number) {
this.orderService
.deleteOrder(id)
.pipe(concatMap(() => this.orderService.getOrders(this.orderPage.number)))
.subscribe((resp) => {
this.orderPage = resp.page;
this.ordersSubj$.next(resp);
});
}
deleteGoods(id: number) {
this.goodsService
.delete(id)
.pipe(concatMap(() => this.goodsService.get(this.goodsPage.number)))
.subscribe((resp) => {
this.goodsPage = resp.page;
this.goodsSubj$.next(resp);
});
}
openOrderModal(order: Order): void {
const orderModalRef = this.modalService.open(OrderEditModalComponent);
orderModalRef.componentInstance.order = order;
orderModalRef.componentInstance.orderChange
.pipe(
take(1),
concatMap(() => this.orderService.getOrders(this.orderPage.number)),
)
.subscribe((resp: PagedResponseModel<Order>) => {
this.orderPage = resp.page;
this.ordersSubj$.next(resp);
});
}
openGoodsModal(product: Goods): void {
const goodsModalRef = this.modalService.open(GoodsEditModalComponent);
goodsModalRef.componentInstance.product = product;
goodsModalRef.componentInstance.isUpdate
.pipe(
filter(Boolean),
concatMap(() => this.goodsService.get(this.goodsPage.number)),
)
.subscribe((resp: PagedResponseModel<Goods>) => {
this.goodsPage = resp.page;
this.goodsSubj$.next(resp);
});
}
openNewProductModal(): void {
const goodsModalRef = this.modalService.open(GoodsEditModalComponent);
goodsModalRef.componentInstance.isUpdate
.pipe(
filter(Boolean),
concatMap(() => this.goodsService.get(this.goodsPage.number)),
)
.subscribe((resp: PagedResponseModel<Goods>) => {
this.goodsPage = resp.page;
this.goodsSubj$.next(resp);
});
}
}
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Edit products</h4>
<button
type="button"
class="btn-close"
aria-label="Close"
(click)="modal?.close()"
></button>
</div>
<div class="modal-body">
@if (goods) {
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col">Count</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@for (product of goods.content; track product.id) {
<tr>
<th scope="row">{{ product.id }}</th>
<td>{{ product.name }}</td>
<td>{{ product.price | currency }}</td>
<td>
<input
type="number"
min="1"
class="w-50 form-control"
[value]="productsCount(product.id)"
#count
/>
</td>
<td>
<button
type="button"
[ngClass]="
productsCount(product.id)
? 'btn-outline-primary'
: 'btn-outline-secondary'
"
class="btn btn-sm me-2"
(click)="addOrUpdate(product.id, +count.value)"
>
{{ productsCount(product.id) ? "Update" : "Add" }}
</button>
</td>
</tr>
}
</tbody>
</table>
@if (goods) {
<ngb-pagination
[page]="goods.page.number + 1"
(pageChange)="pageNumberChange($event - 1)"
[pageSize]="goods.page.size"
[collectionSize]="goods.page.totalElements"
></ngb-pagination>
}
} @else {
loading...
}
<ng-template #loading> loading... </ng-template>
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary" (click)="modal?.close()">
Close
</button>
</div>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddProductModalComponent } from './add-product-modal.component';
describe('AddProductModalComponent', () => {
let component: AddProductModalComponent;
let fixture: ComponentFixture<AddProductModalComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AddProductModalComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AddProductModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { takeUntil } from 'rxjs';
import { AbstractBasicComponent } from '../../../../core/abstract/abstract-component';
import { Goods } from '../../../../core/model/goods.model';
import { Order } from '../../../../core/model/order.model';
import { PagedResponseModel } from '../../../../core/model/paged-response.model';
import { UpdateCount } from '../../order-page.component';
@Component({
selector: 'app-add-product-modal',
templateUrl: './add-product-modal.component.html',
styleUrl: './add-product-modal.component.scss',
})
export class AddProductModalComponent extends AbstractBasicComponent {
@Input() goods!: PagedResponseModel<Goods>;
@Input() order!: Order;
@Output() pageNumber = new EventEmitter<number>();
@Output() orderLineChange = new EventEmitter<UpdateCount>();
modal: NgbModalRef | undefined;
constructor(modalService: NgbModal) {
super();
modalService.activeInstances
.pipe(takeUntil(this.destroy$))
.subscribe((mls) => {
this.modal = mls[mls.length - 1];
});
}
addOrUpdate(goodsId: number, count: number) {
if (count) {
this.orderLineChange.emit({
goodsId,
count,
});
}
}
productsCount(productId: number): number {
return (
this.order?.orderLines.find((ol) => ol.goods.id === productId)?.count ?? 0
);
}
pageNumberChange(page: number) {
this.pageNumber.emit(page);
}
}
<h3>Order Products</h3>
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col">Count</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@for (orderLine of orderLines; let i = $index; track orderLine.id) {
<tr>
<td>{{ i + 1 }}</td>
<td>{{ orderLine.goods.name }}</td>
<td>{{ orderLine.goods.price | currency }}</td>
<td>{{ orderLine.count }}</td>
<td>
<button
type="button"
class="btn btn-sm btn-outline-danger me-2"
(click)="deleteLine(orderLine.id)"
>
Delete
</button>
</td>
</tr>
}
@if (orderLines.length === 0) {
<tr>
<td colspan="5" class="text-center">Nothing</td>
</tr>
}
</tbody>
</table>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderLineListComponent } from './order-line-list.component';
describe('OrderLineListComponent', () => {
let component: OrderLineListComponent;
let fixture: ComponentFixture<OrderLineListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OrderLineListComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OrderLineListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { OrderLine } from '../../../../core/model/order.model';
@Component({
selector: 'app-order-line-list',
templateUrl: './order-line-list.component.html',
styleUrl: './order-line-list.component.scss',
})
export class OrderLineListComponent {
@Input() orderLines!: OrderLine[];
@Output() delete = new EventEmitter<number>();
deleteLine(id: number) {
this.delete.emit(id);
}
}
<ul class="nav">
<li class="nav-item">
<div class="py-2">
<a class="nav-link active" aria-current="page" [routerLink]="'/'"
><i class="bi bi-arrow-left me-2"></i>Back</a
>
</div>
</li>
</ul>
@if (isNew) {
<h3>New order</h3>
<app-order-form></app-order-form>
} @else {
<h3>Order</h3>
@if (orderSubj$ | async; as order) {
<table class="table">
<tr>
<th>Client</th>
<td>{{ order.client }}</td>
</tr>
<tr>
<th>Address</th>
<td>{{ order.address }}</td>
</tr>
<tr>
<th>Date</th>
<td>{{ order.date }}</td>
</tr>
</table>
<div class="py-2 float-end">
<button
class="btn btn-outline-secondary me-2"
(click)="openAddProductModal()"
>
Edit products
</button>
</div>
<app-order-line-list
(delete)="deleteLine($event)"
(add)="openAddProductModal()"
[orderLines]="order.orderLines"
></app-order-line-list>
} @else {
<div>Loading...</div>
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderPageComponent } from './order-page.component';
describe('OrderPageComponent', () => {
let component: OrderPageComponent;
let fixture: ComponentFixture<OrderPageComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OrderPageComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OrderPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subject, concatMap, takeUntil } from 'rxjs';
import { environment } from '../../../environments/environment';
import { AbstractBasicComponent } from '../../core/abstract/abstract-component';
import { Goods } from '../../core/model/goods.model';
import { Order } from '../../core/model/order.model';
import { PagedResponseModel } from '../../core/model/paged-response.model';
import { GoodsService } from '../../core/service/goods.service';
import { OrderLineService } from '../../core/service/order-line.service';
import { OrderService } from '../../core/service/order.service';
import { AddProductModalComponent } from './components/add-product-modal/add-product-modal.component';
const DEV = !environment.production;
@Component({
selector: 'app-order-page',
templateUrl: './order-page.component.html',
styleUrl: './order-page.component.scss',
})
export class OrderPageComponent
extends AbstractBasicComponent
implements OnInit
{
form: FormGroup;
orderSubj$ = new Subject<Order>();
order!: Order;
private id!: number;
constructor(
private orderService: OrderService,
private orderLineService: OrderLineService,
private goodsService: GoodsService,
private modalService: NgbModal,
fb: FormBuilder,
route: ActivatedRoute,
) {
super();
this.form = fb.group({
client: ['', Validators.required],
date: ['', Validators.required],
address: ['', Validators.required],
});
route.params
.pipe(takeUntil(this.destroy$))
.subscribe((params) => (this.id = +params['id']));
}
public get isNew(): boolean {
return this.id === null || isNaN(this.id);
}
ngOnInit(): void {
if (!this.isNew) {
this.orderService.getOrder(this.id).subscribe({
next: (order) => {
this.orderSubj$.next(order);
this.order = order;
},
error: (err) => {
if (DEV) {
console.dir(`Error: ${err}`);
}
},
});
}
}
deleteLine(id: number) {
this.orderLineService
.delete(id)
.pipe(concatMap(() => this.orderService.getOrder(this.id)))
.subscribe({
next: (order: Order) => {
this.orderSubj$.next(order);
this.order = order;
},
error: (err) => {
if (DEV) {
console.dir(`Error: ${err}`);
}
},
});
}
openAddProductModal(): void {
const orderModalRef = this.modalService.open(AddProductModalComponent, {
size: 'xl',
});
orderModalRef.componentInstance.order = this.order;
this.goodsService
.get()
.subscribe((resp) => (orderModalRef.componentInstance.goods = resp));
orderModalRef.componentInstance.pageNumber
.pipe(concatMap((page: number) => this.goodsService.get(page)))
.subscribe((resp: PagedResponseModel<Goods>) => {
orderModalRef.componentInstance.goods = resp;
});
orderModalRef.componentInstance.orderLineChange
.pipe(
takeUntil(this.destroy$),
concatMap((v: UpdateCount) => {
const orderLineId = this.order.orderLines.find(
(ol) => ol.goods.id === v.goodsId,
)?.id;
if (orderLineId) {
return this.orderLineService.put({
id: orderLineId,
orderId: this.order.id,
goodsId: v.goodsId,
count: v.count,
});
}
return this.orderLineService.post({
orderId: this.order.id,
goodsId: v.goodsId,
count: v.count,
});
}),
concatMap(() => this.orderService.getOrder(this.id)),
)
.subscribe({
next: (order: Order) => {
this.orderSubj$.next(order);
this.order = order;
orderModalRef.componentInstance.order = this.order;
},
error: (err: any) => {
if (DEV) {
console.dir(`Error: ${err}`);
}
},
});
}
previousState(): void {
window.history.back();
}
}
export interface UpdateCount {
goodsId: number;
count: number;
}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { IndexPageComponent } from './index-page/index-page.component';
import { OrderPageComponent } from './order-page/order-page.component';
const routes: Routes = [
{path: '', component: IndexPageComponent}
{ path: '', component: IndexPageComponent },
{ path: 'order', component: OrderPageComponent },
{ path: 'order/:id', component: OrderPageComponent },
];
@NgModule({
......
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { PagesRoutingModule } from './pages-routing.module';
import { IndexPageComponent } from './index-page/index-page.component';
import { SharedModule } from '../shared/shared.module';
import { GoodsEditModalComponent } from './index-page/components/goods-edit-modal/goods-edit-modal.component';
import { GoodsListComponent } from './index-page/components/goods-list/goods-list.component';
import { OrderEditModalComponent } from './index-page/components/order-edit-modal/order-edit-modal.component';
import { OrdersListComponent } from './index-page/components/orders-list/orders-list.component';
import { IndexPageComponent } from './index-page/index-page.component';
import { AddProductModalComponent } from './order-page/components/add-product-modal/add-product-modal.component';
import { OrderLineListComponent } from './order-page/components/order-line-list/order-line-list.component';
import { OrderPageComponent } from './order-page/order-page.component';
import { PagesRoutingModule } from './pages-routing.module';
@NgModule({
declarations: [IndexPageComponent, OrdersListComponent],
imports: [CommonModule, PagesRoutingModule],
declarations: [
IndexPageComponent,
OrderPageComponent,
OrdersListComponent,
GoodsListComponent,
OrderLineListComponent,
GoodsEditModalComponent,
AddProductModalComponent,
OrderEditModalComponent,
],
imports: [CommonModule, SharedModule, PagesRoutingModule],
})
export class PagesModule {}
@for (validation of messages; track validation) {
@if (
control.hasError(validation.type) && (control.touched || control.dirty)
) {
{{ validation.message }}
}
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormErrorsComponent } from './form-errors.component';
describe('FormErrorsComponent', () => {
let component: FormErrorsComponent;
let fixture: ComponentFixture<FormErrorsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [FormErrorsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(FormErrorsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Input } from '@angular/core';
import { AbstractControl } from '@angular/forms';
import { ValidationMessage } from '../../core/model/validation.model';
@Component({
selector: 'app-form-errors',
templateUrl: './form-errors.component.html',
styleUrl: './form-errors.component.scss',
})
export class FormErrorsComponent {
@Input() messages!: ValidationMessage[];
@Input() control!: AbstractControl;
}
<form [formGroup]="form">
<div class="mb-3">
<div class="mb-3">
<label for="nameInput" class="form-label">Name</label>
<input
class="form-control"
id="nameInput"
placeholder="John Dow"
formControlName="name"
/>
</div>
<div class="mb-3">
<label for="priceInput" class="form-label">Price</label>
<input
type="number"
step="0.01"
class="form-control"
id="priceInput"
placeholder="123.00"
formControlName="price"
/>
</div>
</div>
</form>
<button type="button" class="btn btn-outline-secondary" (click)="save()">
{{ isNew ? "Create" : "Save" }}
</button>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GoodsFormComponent } from './goods-form.component';
describe('GoodsFormComponent', () => {
let component: GoodsFormComponent;
let fixture: ComponentFixture<GoodsFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GoodsFormComponent]
})
.compileComponents();
fixture = TestBed.createComponent(GoodsFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { environment } from '../../../../environments/environment';
import { Goods } from '../../../core/model/goods.model';
import { GoodsService } from '../../../core/service/goods.service';
const DEV = !environment.production;
@Component({
selector: 'app-goods-form',
templateUrl: './goods-form.component.html',
styleUrl: './goods-form.component.scss',
})
export class GoodsFormComponent {
@Input() product!: Goods;
@Output() productChange = new EventEmitter<Goods>();
@Output() isUpdate = new EventEmitter<boolean>();
form: FormGroup;
constructor(
fb: FormBuilder,
private goodsService: GoodsService,
) {
this.form = fb.group({
name: ['', Validators.required],
price: ['', Validators.required],
});
}
public get isNew(): boolean {
return !this.product;
}
ngOnInit(): void {
if (!this.isNew) {
this.form.setValue({
name: this.product?.name,
price: this.product?.price,
});
}
}
save() {
if (this.form.valid) {
if (this.isNew) {
// Create
this.goodsService.post({ ...this.form.value }).subscribe({
next: (res) => {
this.isUpdate.emit(true);
},
error: (err) => {
if (DEV) {
console.dir(err);
}
},
});
} else {
// Update
this.goodsService
.put({ ...this.form.value, id: this.product.id })
.subscribe({
next: (res) => {
this.productChange.emit(res);
this.isUpdate.emit(true);
},
error: (err) => {
if (DEV) {
console.dir(err);
}
},
});
}
}
}
}
<form [formGroup]="form">
<div class="mb-3">
<div class="mb-3">
<label for="clientInput" class="form-label">Client</label>
<input
class="form-control"
id="clientInput"
placeholder="Client Name"
formControlName="client"
[class.is-invalid]="form.touched && form.get('client')?.invalid"
/>
<div class="invalid-feedback">
<app-form-errors
[control]="form.get('client')!"
[messages]="validationMsgs['client']"
></app-form-errors>
</div>
</div>
<div class="mb-3">
<label for="addressInput" class="form-label">Address</label>
<input
class="form-control"
id="addressInput"
placeholder="Address line"
formControlName="address"
[class.is-invalid]="form.touched && form.get('address')?.invalid"
/>
<div class="invalid-feedback">
<app-form-errors
[control]="form.get('address')!"
[messages]="validationMsgs['address']"
></app-form-errors>
</div>
</div>
<div class="mb-3">
<label for="dateInput" class="form-label">Date</label>
<div class="input-group">
<input
class="form-control"
placeholder="dd/mm/yyyy"
type="text"
ngbDatepicker
#d="ngbDatepicker"
id="addressInput"
formControlName="date"
[class.is-invalid]="form.touched && form.get('date')?.invalid"
/>
<button
class="btn btn-outline-secondary bi bi-calendar3"
(click)="d.toggle()"
type="button"
></button>
</div>
<div class="invalid-feedback">
<app-form-errors
[control]="form.get('date')!"
[messages]="validationMsgs['date']"
></app-form-errors>
</div>
</div>
</div>
<button class="btn btn-outline-primary me-2" (click)="save()">
{{ isNew ? "Create" : "Update" }}
</button>
</form>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OrderFormComponent } from './order-form.component';
describe('OrderFormComponent', () => {
let component: OrderFormComponent;
let fixture: ComponentFixture<OrderFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OrderFormComponent]
})
.compileComponents();
fixture = TestBed.createComponent(OrderFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { environment } from '../../../../environments/environment';
import { Order } from '../../../core/model/order.model';
import { ControlsValidations } from '../../../core/model/validation.model';
import { OrderService } from '../../../core/service/order.service';
const DEV = !environment.production;
@Component({
selector: 'app-order-form',
templateUrl: './order-form.component.html',
styleUrl: './order-form.component.scss',
})
export class OrderFormComponent {
@Input() order!: Order;
@Output() orderChange = new EventEmitter<Order>();
form: FormGroup;
validationMsgs: ControlsValidations;
constructor(
fb: FormBuilder,
private router: Router,
private orderService: OrderService,
) {
this.form = fb.group({
client: ['', Validators.required],
address: ['', Validators.required],
date: ['', Validators.required],
});
this.validationMsgs = {
client: [{ type: 'required', message: 'Required fileld' }],
address: [{ type: 'required', message: 'Required fileld' }],
date: [
{ type: 'required', message: 'Required fileld' },
{ type: 'ngbDate', message: 'Invalid date format' },
],
};
}
public get isNew(): boolean {
return !this.order;
}
save() {
if (this.form.valid) {
if (this.isNew) {
this.orderService.postOrder({ ...this.form.value }).subscribe({
next: (res) => {
this.orderChange.emit(res);
this.router.navigate(['order', res.id]);
},
error: (err) => {
if (DEV) {
console.dir(err);
}
},
});
} else {
this.orderService
.putOrder({ ...this.form.value, id: this.order.id })
.subscribe({
next: (res) => {
this.orderChange.emit(res);
},
error: (err) => {
if (DEV) {
console.dir(err);
}
},
});
}
}
}
ngOnInit(): void {
if (!this.isNew) {
this.form.setValue({
client: this.order.client,
date: this.order.date,
address: this.order.address,
});
}
}
}
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import {
NgbDatepickerModule,
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavItemRole,
NgbNavLinkBase,
NgbNavLinkButton,
NgbNavOutlet,
NgbPagination,
} from '@ng-bootstrap/ng-bootstrap';
import { FormErrorsComponent } from './form-errors/form-errors.component';
import { GoodsFormComponent } from './forms/goods-form/goods-form.component';
import { OrderFormComponent } from './forms/order-form/order-form.component';
@NgModule({
declarations: [],
declarations: [FormErrorsComponent, OrderFormComponent, GoodsFormComponent],
imports: [
CommonModule
]
CommonModule,
FormsModule,
ReactiveFormsModule,
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavItemRole,
NgbNavLinkBase,
NgbNavLinkButton,
NgbNavOutlet,
NgbDatepickerModule,
NgbPagination,
],
exports: [
FormsModule,
ReactiveFormsModule,
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavItemRole,
NgbNavLinkBase,
NgbNavLinkButton,
NgbNavOutlet,
NgbDatepickerModule,
NgbPagination,
FormErrorsComponent,
OrderFormComponent,
GoodsFormComponent,
],
})
export class SharedModule { }
export class SharedModule {}
......@@ -5,5 +5,6 @@ export const environment = {
API_PREFIX: '/api',
ORDER_URL: '/orders',
ORDER_LINE_URL: '/order-line',
GOODS_URL: '/goods',
};
......@@ -5,5 +5,6 @@ export const environment = {
API_PREFIX: '/api',
ORDER_URL: '/orders',
ORDER_LINE_URL: '/order-line',
GOODS_URL: '/goods',
};
/// <reference types="@angular/localize" />
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
......
/* You can add global styles to this file, and also import other style files */
/* Importing Bootstrap SCSS file. */
@import "bootstrap/scss/bootstrap";
......@@ -3,7 +3,9 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
"types": [
"@angular/localize"
]
},
"files": [
"src/main.ts"
......
......@@ -13,7 +13,7 @@
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
......
......@@ -4,7 +4,8 @@
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
"jasmine",
"@angular/localize"
]
},
"include": [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment