Skip to content

Commit e2c7e43

Browse files
authored
Merge pull request #906 from HiEventsDev/develop
2 parents ca2388e + 84f9521 commit e2c7e43

File tree

13 files changed

+1285
-391
lines changed

13 files changed

+1285
-391
lines changed

backend/app/Http/Actions/Orders/Public/CompleteOrderActionPublic.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function __invoke(CompleteOrderRequest $request, int $eventId, string $or
3434
: null,
3535
]),
3636
'products' => $request->input('products'),
37+
'event_id' => $eventId,
3738
]));
3839
} catch (ResourceConflictException $e) {
3940
return $this->errorResponse($e->getMessage(), Response::HTTP_CONFLICT);

backend/app/Services/Application/Handlers/Order/CompleteOrderHandler.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Exception;
99
use HiEvents\DomainObjects\AttendeeDomainObject;
1010
use HiEvents\DomainObjects\Enums\ProductType;
11+
use HiEvents\DomainObjects\EventSettingDomainObject;
1112
use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract;
1213
use HiEvents\DomainObjects\Generated\OrderDomainObjectAbstract;
1314
use HiEvents\DomainObjects\Generated\ProductPriceDomainObjectAbstract;
@@ -24,6 +25,7 @@
2425
use HiEvents\Repository\Eloquent\Value\Relationship;
2526
use HiEvents\Repository\Interfaces\AffiliateRepositoryInterface;
2627
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
28+
use HiEvents\Repository\Interfaces\EventSettingsRepositoryInterface;
2729
use HiEvents\Repository\Interfaces\OrderRepositoryInterface;
2830
use HiEvents\Repository\Interfaces\ProductPriceRepositoryInterface;
2931
use HiEvents\Repository\Interfaces\QuestionAnswerRepositoryInterface;
@@ -55,6 +57,7 @@ public function __construct(
5557
private readonly ProductQuantityUpdateService $productQuantityUpdateService,
5658
private readonly ProductPriceRepositoryInterface $productPriceRepository,
5759
private readonly DomainEventDispatcherService $domainEventDispatcherService,
60+
private readonly EventSettingsRepositoryInterface $eventSettingsRepository,
5861
)
5962
{
6063
}
@@ -90,7 +93,16 @@ public function handle(string $orderShortId, CompleteOrderDTO $orderData): Order
9093
return $updatedOrder;
9194
});
9295

93-
OrderStatusChangedEvent::dispatch($updatedOrder);
96+
/** @var EventSettingDomainObject $eventSettings */
97+
$eventSettings = $this->eventSettingsRepository->findFirstWhere([
98+
'event_id' => $orderData->event_id,
99+
]);
100+
101+
event(new OrderStatusChangedEvent(
102+
order: $updatedOrder,
103+
sendEmails: true,
104+
createInvoice: $eventSettings->getEnableInvoicing(),
105+
));
94106

95107
if ($updatedOrder->isOrderCompleted()) {
96108
$this->domainEventDispatcherService->dispatch(

backend/app/Services/Application/Handlers/Order/DTO/CompleteOrderDTO.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ class CompleteOrderDTO extends BaseDTO
1111
/**
1212
* @param CompleteOrderOrderDTO $order
1313
* @param Collection<CompleteOrderProductDataDTO> $products
14+
* @param int $event_id
1415
*/
1516
public function __construct(
1617
public CompleteOrderOrderDTO $order,
1718
#[CollectionOf(CompleteOrderProductDataDTO::class)]
18-
public Collection $products
19+
public Collection $products,
20+
public int $event_id,
1921
)
2022
{
2123
}

backend/tests/Unit/Services/Application/Handlers/Order/CompleteOrderHandlerTest.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
use Carbon\Carbon;
66
use Exception;
77
use HiEvents\DomainObjects\AttendeeDomainObject;
8+
use HiEvents\DomainObjects\EventSettingDomainObject;
89
use HiEvents\DomainObjects\OrderDomainObject;
910
use HiEvents\DomainObjects\OrderItemDomainObject;
1011
use HiEvents\DomainObjects\ProductPriceDomainObject;
1112
use HiEvents\DomainObjects\Status\OrderStatus;
1213
use HiEvents\Exceptions\ResourceConflictException;
1314
use HiEvents\Repository\Interfaces\AffiliateRepositoryInterface;
1415
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
16+
use HiEvents\Repository\Interfaces\EventSettingsRepositoryInterface;
1517
use HiEvents\Repository\Interfaces\OrderRepositoryInterface;
1618
use HiEvents\Repository\Interfaces\ProductPriceRepositoryInterface;
1719
use HiEvents\Repository\Interfaces\QuestionAnswerRepositoryInterface;
@@ -44,6 +46,7 @@ class CompleteOrderHandlerTest extends TestCase
4446
private CompleteOrderHandler $completeOrderHandler;
4547
private DomainEventDispatcherService $domainEventDispatcherService;
4648
private AffiliateRepositoryInterface|MockInterface $affiliateRepository;
49+
private EventSettingsRepositoryInterface $eventSettingsRepository;
4750

4851
protected function setUp(): void
4952
{
@@ -60,6 +63,7 @@ protected function setUp(): void
6063
$this->productPriceRepository = Mockery::mock(ProductPriceRepositoryInterface::class);
6164
$this->domainEventDispatcherService = Mockery::mock(DomainEventDispatcherService::class);
6265
$this->affiliateRepository = Mockery::mock(AffiliateRepositoryInterface::class);
66+
$this->eventSettingsRepository = Mockery::mock(EventSettingsRepositoryInterface::class);
6367

6468
$this->completeOrderHandler = new CompleteOrderHandler(
6569
$this->orderRepository,
@@ -68,7 +72,8 @@ protected function setUp(): void
6872
$this->questionAnswersRepository,
6973
$this->productQuantityUpdateService,
7074
$this->productPriceRepository,
71-
$this->domainEventDispatcherService
75+
$this->domainEventDispatcherService,
76+
$this->eventSettingsRepository,
7277
);
7378
}
7479

@@ -97,6 +102,8 @@ public function testHandleSuccessfullyCompletesOrder(): void
97102

98103
$this->productQuantityUpdateService->shouldReceive('updateQuantitiesFromOrder');
99104

105+
$this->eventSettingsRepository->shouldReceive('findFirstWhere')->andReturn($this->createMockEventSetting());
106+
100107
$this->completeOrderHandler->handle($orderShortId, $orderData);
101108

102109
$this->assertTrue(true);
@@ -169,6 +176,8 @@ public function testHandleUpdatesProductQuantitiesForFreeOrder(): void
169176

170177
$this->productQuantityUpdateService->shouldReceive('updateQuantitiesFromOrder')->once();
171178

179+
$this->eventSettingsRepository->shouldReceive('findFirstWhere')->andReturn($this->createMockEventSetting());
180+
172181
$this->domainEventDispatcherService->shouldReceive('dispatch')
173182
->withArgs(function (OrderEvent $event) use ($order) {
174183
return $event->type === DomainEventType::ORDER_CREATED
@@ -201,6 +210,8 @@ public function testHandleDoesNotUpdateProductQuantitiesForPaidOrder(): void
201210

202211
$this->productQuantityUpdateService->shouldNotReceive('updateQuantitiesFromOrder');
203212

213+
$this->eventSettingsRepository->shouldReceive('findFirstWhere')->andReturn($this->createMockEventSetting());
214+
204215
$this->completeOrderHandler->handle($orderShortId, $orderData);
205216

206217
$this->expectNotToPerformAssertions();
@@ -269,6 +280,7 @@ private function createMockCompleteOrderDTO(): CompleteOrderDTO
269280
return new CompleteOrderDTO(
270281
order: $orderDTO,
271282
products: new Collection([$attendeeDTO])
283+
,event_id: 1
272284
);
273285
}
274286

@@ -313,4 +325,11 @@ private function createMockAttendee(): AttendeeDomainObject|MockInterface
313325
$attendee->shouldReceive('getProductId')->andReturn(1);
314326
return $attendee;
315327
}
328+
329+
private function createMockEventSetting(): EventSettingDomainObject
330+
{
331+
return (new EventSettingDomainObject())
332+
->setId(1)
333+
->setEventId(1);
334+
}
316335
}

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@stripe/stripe-js": "^1.54.1",
4343
"@tabler/icons-react": "^2.44.0",
4444
"@tanstack/react-query": "5.76.1",
45+
"@tanstack/react-table": "^8.21.3",
4546
"@tiptap/core": "^2.7.0",
4647
"@tiptap/extension-color": "^2.11.7",
4748
"@tiptap/extension-image": "^2.11.5",
Lines changed: 137 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,148 @@
1-
.tableContainer {
2-
overflow-x: auto;
1+
.attendeeDetails {
2+
min-width: 0;
3+
flex: 1;
4+
display: flex;
5+
flex-direction: column;
6+
gap: 6px;
37
}
48

5-
.table {
6-
width: 100%;
7-
position: relative;
9+
.nameRow {
10+
display: flex;
11+
align-items: center;
12+
gap: 8px;
813
}
914

10-
.stickyActionColumn {
11-
position: sticky;
12-
right: 0;
13-
background: white;
14-
box-shadow: -4px 0 6px rgba(0, 0, 0, 0.1);
15-
z-index: 2;
15+
.attendeeName {
16+
font-size: 15px;
17+
font-weight: 600;
18+
line-height: 1.3;
19+
text-decoration: none;
20+
color: var(--mantine-color-text);
21+
22+
&:hover {
23+
text-decoration: underline;
24+
}
25+
}
26+
27+
.attendeeId {
28+
font-size: 12px;
29+
color: var(--mantine-color-dimmed);
30+
font-family: 'Courier New', monospace;
31+
line-height: 1.3;
32+
}
33+
34+
.emailRow {
35+
display: inline-flex;
36+
align-items: center;
37+
gap: 8px;
38+
}
39+
40+
.attendeeEmail {
41+
font-size: 13px;
42+
line-height: 1.3;
43+
color: var(--mantine-color-dimmed);
44+
overflow: hidden;
45+
text-overflow: ellipsis;
46+
white-space: nowrap;
47+
max-width: 250px;
48+
}
49+
50+
.emailActions {
51+
display: inline-flex;
52+
align-items: center;
53+
gap: 4px;
54+
flex-shrink: 0;
55+
}
56+
57+
.actionIcon {
58+
flex-shrink: 0;
59+
}
60+
61+
.orderTicketContainer {
62+
display: flex;
63+
flex-direction: column;
64+
gap: 6px;
1665
}
1766

18-
.tableContainer::-webkit-scrollbar {
19-
height: 10px;
67+
.ticketName {
68+
font-size: 14px;
69+
font-weight: 500;
70+
color: var(--mantine-color-text);
71+
line-height: 1.3;
72+
overflow: hidden;
73+
text-overflow: ellipsis;
74+
white-space: nowrap;
2075
}
2176

22-
.tableContainer::-webkit-scrollbar-thumb {
23-
background: rgba(0, 0, 0, 0.2);
24-
border-radius: 5px;
77+
.orderId {
78+
font-size: 11px;
79+
color: var(--mantine-color-dimmed);
80+
font-family: 'Courier New', monospace;
81+
line-height: 1.3;
82+
83+
:hover {
84+
text-decoration: underline;
85+
}
86+
}
87+
88+
.registrationDate {
89+
font-size: 12px;
90+
color: var(--mantine-color-dimmed);
91+
line-height: 1.3;
92+
}
93+
94+
.checkInButton {
95+
display: inline-flex;
96+
align-items: center;
97+
gap: 6px;
98+
padding: 8px 14px;
99+
border: 1.5px solid var(--mantine-color-default-border);
100+
border-radius: 8px;
101+
background: var(--mantine-color-body);
102+
font-size: 13px;
103+
font-weight: 500;
104+
cursor: pointer;
105+
white-space: nowrap;
106+
107+
&.checkedIn {
108+
border-color: var(--mantine-color-green-6);
109+
background: var(--mantine-color-green-0);
110+
color: var(--mantine-color-green-9);
111+
}
112+
113+
&.notCheckedIn {
114+
color: var(--mantine-color-dimmed);
115+
}
116+
}
117+
118+
.statusBadge {
119+
display: inline-flex;
120+
align-items: center;
121+
gap: 4px;
122+
padding: 6px 12px;
123+
border-radius: 6px;
124+
font-size: 12px;
125+
font-weight: 600;
126+
white-space: nowrap;
127+
128+
&[data-status="ACTIVE"] {
129+
background: var(--mantine-color-green-1);
130+
color: var(--mantine-color-green-9);
131+
}
132+
133+
&[data-status="AWAITING_PAYMENT"] {
134+
background: var(--mantine-color-yellow-1);
135+
color: var(--mantine-color-yellow-9);
136+
}
137+
138+
&[data-status="CANCELLED"] {
139+
background: var(--mantine-color-red-1);
140+
color: var(--mantine-color-red-9);
141+
}
25142
}
26143

27-
.tableContainer::-webkit-scrollbar-track {
28-
background: rgba(0, 0, 0, 0.05);
144+
.actionsMenu {
145+
display: flex;
146+
align-items: center;
147+
justify-content: flex-end;
29148
}

0 commit comments

Comments
 (0)