Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ on:
- development
- production


# Actions
jobs:
deploy:

runs-on: ubuntu-latest
runs-on: ubuntu-latest
steps:
- name: Set build profile

Expand All @@ -36,15 +34,15 @@ jobs:
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Docker-compose build
shell: bash
run: docker-compose -f .deploy/docker-compose.build.yml --profile ${PROFILE} build

- name: Docker-compose push
shell: bash
run: docker-compose -f .deploy/docker-compose.build.yml --profile ${PROFILE} push
shell: bash
run: docker-compose -f .deploy/docker-compose.build.yml --profile ${PROFILE} push

- name: Execute docker-compose up through SSH
uses: appleboy/ssh-action@master
Expand Down
36 changes: 35 additions & 1 deletion apps/admin-page-e2e/src/integration/pages/orders.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,12 @@ const mockOrders = [
describe('ordersPage', () => {
beforeEach(() => {
localStorage.setItem(LocalStorageVars.authUser, JSON.stringify(authMockService.getMockUser(AuthUserEnum.authUser)));

cy.intercept('GET', 'localhost:5000/orders', {
body: mockOrders,
statusCode: 200,
});
}).as('fetchOrders');

cy.visit('/orders');
});

Expand Down Expand Up @@ -247,4 +249,36 @@ describe('ordersPage', () => {
});
});
});

it('should display a list of options', () => {
cy.get('.mat-select-panel').should('not.exist');
cy.get('[data-cy=order-status]').first().click();
cy.get('.mat-select-panel').should('be.visible');
cy.get('[data-cy=order-status-option]').should('have.length', 8);
});

it('should correctly change the status', () => {
cy.intercept('PATCH', 'localhost:5000/orders/status/MakeMeWantIt', {
body: mockOrder(OrderStatusEnum.delivered, 14),
statusCode: 200,
}).as('updateOrderStatus');

cy.get('[data-cy=order-status]').first().contains('INITIAL');
cy.get('[data-cy=order-status]').first().click();
cy.get('[data-cy=order-status-option]').first().click();
cy.get('[data-cy=order-status]').first().contains('DELIVERED');
cy.wait('@updateOrderStatus').its('request.body').should('include', { status: 'DELIVERED' });
});

it('should reload orders on status change failure', () => {
cy.intercept('PATCH', 'localhost:5000/orders/status/MakeMeWantIt', {
statusCode: 500,
}).as('updateOrderStatus');

cy.get('[data-cy=order-status]').first().click();
cy.get('[data-cy=order-status-option]').first().click();

cy.wait('@fetchOrders');
cy.get('[data-cy=order-status]').first().contains('INITIAL');
});
});
2 changes: 2 additions & 0 deletions apps/admin-page/src/app/material.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { MatListModule } from '@angular/material/list';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatCardModule } from '@angular/material/card';
import { MatTableModule } from '@angular/material/table';
import { MatSelectModule } from '@angular/material/select';

export const materialModules = [
MatCommonModule,
Expand All @@ -27,4 +28,5 @@ export const materialModules = [
MatProgressSpinnerModule,
MatFormFieldModule,
MatSnackBarModule,
MatSelectModule,
];
31 changes: 31 additions & 0 deletions apps/admin-page/src/app/pages/orders/orders.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,34 @@ button {
padding: 0 6px;
border-radius: 2px;
}

.order-status-select {
width: 10vw;
height: 2.3vh;
min-width: 8.1vw;
min-height: 15px;
padding: 0 6px;
border-radius: 3px;
}

::ng-deep table .mat-select-value {
color: white;
}

::ng-deep .mat-option-text {
color: white;
}

mat-option {
width: 10vw;
min-width: 8.1vw;
padding: 0 6px;
}

::ng-deep .mat-select-panel {
min-width: 10vw !important;
}

::ng-deep table .mat-select-arrow {
color: white !important;
}
18 changes: 13 additions & 5 deletions apps/admin-page/src/app/pages/orders/orders.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,21 @@
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef>Status</th>
<td mat-cell *matCellDef="let order">
<div
class="label"
[ngStyle]="{ 'background-color': getOrderStatusColor(order.status) }"
<mat-select
[(value)]="order.status"
class="order-status-select"
data-cy="order-status"
[ngStyle]="{ 'background-color': getOrderStatusColor(order.status) }"
(selectionChange)="onStatusChange(order)"
>
{{ order.status }}
</div>
<mat-option
[value]="status"
*ngFor="let status of orderStatusOptions"
[ngStyle]="{ 'background-color': getOrderStatusColor(status) }"
data-cy="order-status-option"
>{{ status }}</mat-option
>
</mat-select>
</td>
</ng-container>

Expand Down
26 changes: 26 additions & 0 deletions apps/admin-page/src/app/pages/orders/orders.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ export class OrdersComponent implements OnInit {
'status',
'actions',
];
orderStatusOptions: OrderStatusEnum[] = [
OrderStatusEnum.delivered,
OrderStatusEnum.assembling,
OrderStatusEnum.shipped,
OrderStatusEnum.pending,
OrderStatusEnum.initial,
OrderStatusEnum.processed,
OrderStatusEnum.new,
OrderStatusEnum.rejected,
];
orders!: IOrder[];

constructor(private ordersService: OrdersService) {}
Expand All @@ -27,6 +37,22 @@ export class OrdersComponent implements OnInit {
this.fetchOrders();
}

/**
* Gets triggered when the status of an order has been changed.\
* It will call the API to update the order with its new status.\
* In case an error is encountered, the orders will be reloaded from the database.
*
* @param order - the order containing the new status.
*/
onStatusChange(order: IOrder): void {
this.ordersService.updateOrder(order).subscribe({
error: (error: HttpErrorResponse) => {
this.fetchOrders();
console.error(error);
},
});
}

/**
* Fetches the orders from the API.\
* \
Expand Down
15 changes: 15 additions & 0 deletions apps/admin-page/src/app/services/orders/orders.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,22 @@ import { environment as env } from '../../../environments/environment';
export class OrdersService {
constructor(private http: HttpClient) {}

/**
* Calls the API to fetch all available orders.
*
* @returns an observable with the list of orders.
*/
public getOrders(): Observable<IOrder[]> {
return this.http.get<IOrder[]>(`${env.apiUrl}/orders`);
}

/**
* Calls the API to update the status of an order.
*
* @param order - the order that needs to be updated.
* @returns an observable with the updated order.
*/
public updateOrder(order: IOrder): Observable<IOrder> {
return this.http.patch<IOrder>(`${env.apiUrl}/orders/status/${order.orderId}`, { status: order.status });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import dk.treecreate.api.order.dto.CreateOrderRequest;
import dk.treecreate.api.order.dto.GetAllOrdersResponse;
import dk.treecreate.api.order.dto.GetOrdersResponse;
import dk.treecreate.api.order.dto.UpdateOrderStatusRequest;
import dk.treecreate.api.transactionitem.TransactionItemRepository;
import dk.treecreate.api.user.User;
import dk.treecreate.api.user.UserRepository;
Expand All @@ -16,6 +17,7 @@
import dk.treecreate.api.utils.model.quickpay.dto.CreatePaymentLinkResponse;
import io.sentry.Sentry;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -33,6 +35,7 @@
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
Expand Down Expand Up @@ -159,4 +162,36 @@ public CreatePaymentLinkResponse createPayment(
Sentry.captureMessage("New order has been created");
return createPaymentLinkResponse;
}

/**
* Attempts to update the order with the ID received as a path parameter
* with the new status received in the body.
* Will return a response with the full order if it is successful or 404 - Not Found
* if there is no order with specified id.
*
* @param updateOrderStatusRequest DTO for the request.
* @param orderId the ID of the order.
* @return the updated order.
*/
@PatchMapping("/status/{orderId}")
@Operation(summary = "Update an order's status")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Updated the orders's status",
response = Order.class)
})
@PreAuthorize("hasRole('DEVELOPER') or hasRole('ADMIN')")
public Order updateOrderStatus(
@RequestBody() @Valid UpdateOrderStatusRequest updateOrderStatusRequest,
@ApiParam(name = "orderId", example = "c0a80121-7ac0-190b-817a-c08ab0a12345")
@PathVariable UUID orderId)
{
try
{
return orderService.updateOrderStatus(orderId, updateOrderStatusRequest.getStatus());
} catch (ResourceNotFoundException e)
{
throw new ResourceNotFoundException("Order not found");
}
}

}
20 changes: 18 additions & 2 deletions apps/api/src/main/java/dk/treecreate/api/order/OrderService.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ public boolean verifyPrice(Order order)
return true;
}

/**
* Updates the order with the provided ID to contain the new status.
*
* @param orderId the ID of the order.
* @param status the new status of the order.
* @return the updated order.
*/
public Order updateOrderStatus(UUID orderId, OrderStatus status)
throws ResourceNotFoundException
{
Order order = orderRepository.findByOrderId(orderId)
.orElseThrow(() -> new ResourceNotFoundException("Order not found"));

order.setStatus(status);
return orderRepository.save(order);
}

public BigDecimal calculateTotal(BigDecimal subTotal, Discount discount, boolean hasMoreThan3)
{
BigDecimal total = subTotal;
Expand Down Expand Up @@ -252,8 +269,7 @@ public Order setupOrderFromCreateRequest(CreateOrderRequest createOrderRequest)
User user = userRepository.findByEmail(userDetails.getUsername())
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
order.setUserId(user.getUserId());
for (
UUID itemId : createOrderRequest.getTransactionItemIds())
for (UUID itemId : createOrderRequest.getTransactionItemIds())
{
TransactionItem transactionItem =
transactionItemRepository.findByTransactionItemId(itemId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dk.treecreate.api.order.dto;

import dk.treecreate.api.utils.OrderStatus;
import io.swagger.annotations.ApiModelProperty;

import javax.validation.constraints.NotNull;

public class UpdateOrderStatusRequest
{
@NotNull
@ApiModelProperty(notes = "Status of the order", example = "PENDING",
required = true)
private OrderStatus status;

public OrderStatus getStatus()
{
return status;
}

public void setStatus(OrderStatus status)
{
this.status = status;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
public class CreatePaymentLinkRequest
{
public int amount;
// no floating points, multiply the values by 100 to get two-points of precision!
// no floating points, multiply the values by 100 to get two-points of precision!
public String language;
public String continue_url;
public String cancel_url;
Expand Down
Loading