Skip to content

Commit 112778a

Browse files
daveearleydalsoopclaudealbanobattistellaViburnum
authored
Check-in UX improvements (#1175)
Co-authored-by: dalsoop <devops@prelik.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Dave Earley <dave@hi.events> Co-authored-by: albanobattistella <34811668+albanobattistella@users.noreply.github.com> Co-authored-by: Florian Meyer <Viburnum@users.noreply.github.com> Co-authored-by: Right Jeong <team@dalsoop.com> Co-authored-by: mrjbj <jason@brucejones.biz> fix(LanguageSwitcher): add default case to prevent SSR crash on unhandled locale (#1170)
1 parent 458a27c commit 112778a

146 files changed

Lines changed: 22010 additions & 22359 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace HiEvents\DomainObjects\Enums;
4+
5+
enum TrackingPixelProvider: string
6+
{
7+
use BaseEnum;
8+
9+
case FACEBOOK_PIXEL = 'facebook_pixel';
10+
case GOOGLE_ANALYTICS_4 = 'google_analytics_4';
11+
case GOOGLE_TAG_MANAGER = 'google_tag_manager';
12+
case TIKTOK_PIXEL = 'tiktok_pixel';
13+
14+
public function pixelIdPattern(): string
15+
{
16+
return match ($this) {
17+
self::FACEBOOK_PIXEL => '/^\d{9,20}$/',
18+
self::GOOGLE_ANALYTICS_4 => '/^G-[a-zA-Z0-9]{6,20}$/',
19+
self::GOOGLE_TAG_MANAGER => '/^GTM-[a-zA-Z0-9]{4,20}$/',
20+
self::TIKTOK_PIXEL => '/^[a-zA-Z0-9]{6,30}$/',
21+
};
22+
}
23+
24+
public function pixelIdFormatDescription(): string
25+
{
26+
return match ($this) {
27+
self::FACEBOOK_PIXEL => __('Must be 9-20 digits (e.g., 1234567890)'),
28+
self::GOOGLE_ANALYTICS_4 => __('Must start with G- followed by 6-20 characters (e.g., G-XXXXXXXXXX)'),
29+
self::GOOGLE_TAG_MANAGER => __('Must start with GTM- followed by 4-20 characters (e.g., GTM-XXXXXXX)'),
30+
self::TIKTOK_PIXEL => __('Must be 6-30 alphanumeric characters'),
31+
};
32+
}
33+
}

backend/app/DomainObjects/Generated/CheckInListDomainObjectAbstract.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ abstract class CheckInListDomainObjectAbstract extends \HiEvents\DomainObjects\A
2020
final public const DELETED_AT = 'deleted_at';
2121
final public const CREATED_AT = 'created_at';
2222
final public const UPDATED_AT = 'updated_at';
23+
final public const PUBLIC_SHOW_ATTENDEE_NOTES = 'public_show_attendee_notes';
24+
final public const PUBLIC_SHOW_QUESTION_ANSWERS = 'public_show_question_answers';
25+
final public const PUBLIC_SHOW_ORDER_DETAILS = 'public_show_order_details';
2326

2427
protected int $id;
2528
protected int $event_id;
@@ -31,6 +34,9 @@ abstract class CheckInListDomainObjectAbstract extends \HiEvents\DomainObjects\A
3134
protected ?string $deleted_at = null;
3235
protected ?string $created_at = null;
3336
protected ?string $updated_at = null;
37+
protected bool $public_show_attendee_notes = true;
38+
protected bool $public_show_question_answers = true;
39+
protected bool $public_show_order_details = true;
3440

3541
public function toArray(): array
3642
{
@@ -45,6 +51,9 @@ public function toArray(): array
4551
'deleted_at' => $this->deleted_at ?? null,
4652
'created_at' => $this->created_at ?? null,
4753
'updated_at' => $this->updated_at ?? null,
54+
'public_show_attendee_notes' => $this->public_show_attendee_notes ?? null,
55+
'public_show_question_answers' => $this->public_show_question_answers ?? null,
56+
'public_show_order_details' => $this->public_show_order_details ?? null,
4857
];
4958
}
5059

@@ -157,4 +166,37 @@ public function getUpdatedAt(): ?string
157166
{
158167
return $this->updated_at;
159168
}
169+
170+
public function setPublicShowAttendeeNotes(bool $public_show_attendee_notes): self
171+
{
172+
$this->public_show_attendee_notes = $public_show_attendee_notes;
173+
return $this;
174+
}
175+
176+
public function getPublicShowAttendeeNotes(): bool
177+
{
178+
return $this->public_show_attendee_notes;
179+
}
180+
181+
public function setPublicShowQuestionAnswers(bool $public_show_question_answers): self
182+
{
183+
$this->public_show_question_answers = $public_show_question_answers;
184+
return $this;
185+
}
186+
187+
public function getPublicShowQuestionAnswers(): bool
188+
{
189+
return $this->public_show_question_answers;
190+
}
191+
192+
public function setPublicShowOrderDetails(bool $public_show_order_details): self
193+
{
194+
$this->public_show_order_details = $public_show_order_details;
195+
return $this;
196+
}
197+
198+
public function getPublicShowOrderDetails(): bool
199+
{
200+
return $this->public_show_order_details;
201+
}
160202
}

backend/app/DomainObjects/Generated/OrganizerSettingDomainObjectAbstract.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ abstract class OrganizerSettingDomainObjectAbstract extends \HiEvents\DomainObje
2929
final public const DEFAULT_SHOW_MARKETING_OPT_IN = 'default_show_marketing_opt_in';
3030
final public const DEFAULT_PASS_PLATFORM_FEE_TO_BUYER = 'default_pass_platform_fee_to_buyer';
3131
final public const DEFAULT_ALLOW_ATTENDEE_SELF_EDIT = 'default_allow_attendee_self_edit';
32+
final public const TRACKING_PIXELS = 'tracking_pixels';
33+
final public const TRACKING_CONSENT_ACKNOWLEDGED = 'tracking_consent_acknowledged';
3234

3335
protected int $id;
3436
protected int $organizer_id;
@@ -49,6 +51,8 @@ abstract class OrganizerSettingDomainObjectAbstract extends \HiEvents\DomainObje
4951
protected bool $default_show_marketing_opt_in = true;
5052
protected bool $default_pass_platform_fee_to_buyer = false;
5153
protected bool $default_allow_attendee_self_edit = true;
54+
protected array|string|null $tracking_pixels = null;
55+
protected bool $tracking_consent_acknowledged = false;
5256

5357
public function toArray(): array
5458
{
@@ -72,6 +76,8 @@ public function toArray(): array
7276
'default_show_marketing_opt_in' => $this->default_show_marketing_opt_in ?? null,
7377
'default_pass_platform_fee_to_buyer' => $this->default_pass_platform_fee_to_buyer ?? null,
7478
'default_allow_attendee_self_edit' => $this->default_allow_attendee_self_edit ?? null,
79+
'tracking_pixels' => $this->tracking_pixels ?? null,
80+
'tracking_consent_acknowledged' => $this->tracking_consent_acknowledged ?? null,
7581
];
7682
}
7783

@@ -284,4 +290,26 @@ public function getDefaultAllowAttendeeSelfEdit(): bool
284290
{
285291
return $this->default_allow_attendee_self_edit;
286292
}
293+
294+
public function setTrackingPixels(array|string|null $tracking_pixels): self
295+
{
296+
$this->tracking_pixels = $tracking_pixels;
297+
return $this;
298+
}
299+
300+
public function getTrackingPixels(): array|string|null
301+
{
302+
return $this->tracking_pixels;
303+
}
304+
305+
public function setTrackingConsentAcknowledged(bool $tracking_consent_acknowledged): self
306+
{
307+
$this->tracking_consent_acknowledged = $tracking_consent_acknowledged;
308+
return $this;
309+
}
310+
311+
public function getTrackingConsentAcknowledged(): bool
312+
{
313+
return $this->tracking_consent_acknowledged;
314+
}
287315
}

backend/app/DomainObjects/Generated/WebhookDomainObjectAbstract.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ abstract class WebhookDomainObjectAbstract extends \HiEvents\DomainObjects\Abstr
1313
final public const ID = 'id';
1414
final public const USER_ID = 'user_id';
1515
final public const EVENT_ID = 'event_id';
16-
final public const ORGANIZER_ID = 'organizer_id';
1716
final public const ACCOUNT_ID = 'account_id';
17+
final public const ORGANIZER_ID = 'organizer_id';
1818
final public const URL = 'url';
1919
final public const EVENT_TYPES = 'event_types';
2020
final public const LAST_RESPONSE_CODE = 'last_response_code';
@@ -29,8 +29,8 @@ abstract class WebhookDomainObjectAbstract extends \HiEvents\DomainObjects\Abstr
2929
protected int $id;
3030
protected int $user_id;
3131
protected ?int $event_id = null;
32-
protected ?int $organizer_id = null;
3332
protected int $account_id;
33+
protected ?int $organizer_id = null;
3434
protected string $url;
3535
protected array|string $event_types;
3636
protected ?int $last_response_code = null;
@@ -48,8 +48,8 @@ public function toArray(): array
4848
'id' => $this->id ?? null,
4949
'user_id' => $this->user_id ?? null,
5050
'event_id' => $this->event_id ?? null,
51-
'organizer_id' => $this->organizer_id ?? null,
5251
'account_id' => $this->account_id ?? null,
52+
'organizer_id' => $this->organizer_id ?? null,
5353
'url' => $this->url ?? null,
5454
'event_types' => $this->event_types ?? null,
5555
'last_response_code' => $this->last_response_code ?? null,
@@ -96,26 +96,26 @@ public function getEventId(): ?int
9696
return $this->event_id;
9797
}
9898

99-
public function setOrganizerId(?int $organizer_id): self
99+
public function setAccountId(int $account_id): self
100100
{
101-
$this->organizer_id = $organizer_id;
101+
$this->account_id = $account_id;
102102
return $this;
103103
}
104104

105-
public function getOrganizerId(): ?int
105+
public function getAccountId(): int
106106
{
107-
return $this->organizer_id;
107+
return $this->account_id;
108108
}
109109

110-
public function setAccountId(int $account_id): self
110+
public function setOrganizerId(?int $organizer_id): self
111111
{
112-
$this->account_id = $account_id;
112+
$this->organizer_id = $organizer_id;
113113
return $this;
114114
}
115115

116-
public function getAccountId(): int
116+
public function getOrganizerId(): ?int
117117
{
118-
return $this->account_id;
118+
return $this->organizer_id;
119119
}
120120

121121
public function setUrl(string $url): self

backend/app/Http/Actions/CheckInLists/CreateCheckInListAction.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ public function __invoke(UpsertCheckInListRequest $request, int $eventId): JsonR
3333
productIds: $request->validated('product_ids'),
3434
expiresAt: $request->validated('expires_at'),
3535
activatesAt: $request->validated('activates_at'),
36+
publicShowAttendeeNotes: $request->validated('public_show_attendee_notes') ?? true,
37+
publicShowQuestionAnswers: $request->validated('public_show_question_answers') ?? true,
38+
publicShowOrderDetails: $request->validated('public_show_order_details') ?? true,
3639
)
3740
);
3841
} catch (UnrecognizedProductIdException $exception) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace HiEvents\Http\Actions\CheckInLists\Public;
4+
5+
use HiEvents\Http\Actions\BaseAction;
6+
use HiEvents\Resources\Attendee\AttendeeDetailPublicResource;
7+
use HiEvents\Services\Application\Handlers\CheckInList\Public\GetCheckInListAttendeeDetailPublicHandler;
8+
use HiEvents\Services\Domain\Auth\AuthUserService;
9+
use Illuminate\Http\JsonResponse;
10+
use Throwable;
11+
12+
class GetCheckInListAttendeeDetailPublicAction extends BaseAction
13+
{
14+
public function __construct(
15+
private readonly GetCheckInListAttendeeDetailPublicHandler $handler,
16+
private readonly AuthUserService $authUserService,
17+
)
18+
{
19+
}
20+
21+
public function __invoke(string $checkInListShortId, string $attendeePublicId): JsonResponse
22+
{
23+
$detail = $this->handler->handle(
24+
shortId: $checkInListShortId,
25+
attendeePublicId: $attendeePublicId,
26+
staffAccountId: $this->resolveStaffAccountId(),
27+
);
28+
29+
return $this->resourceResponse(
30+
resource: AttendeeDetailPublicResource::class,
31+
data: $detail,
32+
);
33+
}
34+
35+
/**
36+
* The detail endpoint is public but should reveal all attendee fields to authenticated staff
37+
* whose account matches the event's account. Returns null for anonymous / invalid tokens /
38+
* any auth resolution failure — those callers get data filtered by the list's visibility flags.
39+
*/
40+
private function resolveStaffAccountId(): ?int
41+
{
42+
try {
43+
return $this->authUserService->getAuthenticatedAccountId();
44+
} catch (Throwable) {
45+
return null;
46+
}
47+
}
48+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace HiEvents\Http\Actions\CheckInLists\Public;
4+
5+
use HiEvents\Http\Actions\BaseAction;
6+
use HiEvents\Resources\CheckInList\CheckInListStatsPublicResource;
7+
use HiEvents\Services\Application\Handlers\CheckInList\Public\GetCheckInListStatsPublicHandler;
8+
use Illuminate\Http\JsonResponse;
9+
10+
class GetCheckInListStatsPublicAction extends BaseAction
11+
{
12+
public function __construct(
13+
private readonly GetCheckInListStatsPublicHandler $getCheckInListStatsPublicHandler,
14+
)
15+
{
16+
}
17+
18+
public function __invoke(string $checkInListShortId): JsonResponse
19+
{
20+
$stats = $this->getCheckInListStatsPublicHandler->handle($checkInListShortId);
21+
22+
return $this->resourceResponse(
23+
resource: CheckInListStatsPublicResource::class,
24+
data: $stats,
25+
);
26+
}
27+
}

backend/app/Http/Actions/CheckInLists/UpdateCheckInListAction.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public function __invoke(UpsertCheckInListRequest $request, int $eventId, int $c
3434
expiresAt: $request->validated('expires_at'),
3535
activatesAt: $request->validated('activates_at'),
3636
id: $checkInListId,
37+
publicShowAttendeeNotes: $request->validated('public_show_attendee_notes') ?? true,
38+
publicShowQuestionAnswers: $request->validated('public_show_question_answers') ?? true,
39+
publicShowOrderDetails: $request->validated('public_show_order_details') ?? true,
3740
)
3841
);
3942
} catch (UnrecognizedProductIdException $exception) {

backend/app/Http/Actions/Events/Stats/GetEventStatsAction.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace HiEvents\Http\Actions\Events\Stats;
44

5-
use Carbon\Carbon;
65
use HiEvents\DomainObjects\EventDomainObject;
76
use HiEvents\Http\Actions\BaseAction;
87
use HiEvents\Services\Application\Handlers\Event\DTO\EventStatsRequestDTO;
98
use HiEvents\Services\Application\Handlers\Event\GetEventStatsHandler;
109
use Illuminate\Http\JsonResponse;
10+
use Illuminate\Http\Request;
1111
use Illuminate\Http\Resources\Json\JsonResource;
1212

1313
class GetEventStatsAction extends BaseAction
@@ -18,14 +18,15 @@ public function __construct(
1818
{
1919
}
2020

21-
public function __invoke(int $eventId): JsonResponse
21+
public function __invoke(int $eventId, Request $request): JsonResponse
2222
{
2323
$this->isActionAuthorized($eventId, EventDomainObject::class);
2424

25+
$dateRangePreset = $request->query('date_range', 'month');
26+
2527
$stats = $this->eventStatsHandler->handle(EventStatsRequestDTO::fromArray([
2628
'event_id' => $eventId,
27-
'start_date' => Carbon::now()->subDays(7)->format('Y-m-d H:i:s'),
28-
'end_date' => Carbon::now()->format('Y-m-d H:i:s')
29+
'date_range_preset' => $dateRangePreset,
2930
]));
3031

3132
return $this->resourceResponse(JsonResource::class, $stats);

backend/app/Http/Request/CheckInList/UpsertCheckInListRequest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ public function rules(): array
1111
{
1212
return [
1313
'name' => RulesHelper::REQUIRED_STRING,
14-
'description' => ['nullable', 'string', 'max:255'],
14+
'description' => ['nullable', 'string', 'max:2000'],
1515
'expires_at' => ['nullable', 'date'],
1616
'activates_at' => ['nullable', 'date'],
1717
'product_ids' => ['required', 'array', 'min:1'],
18+
'public_show_attendee_notes' => ['nullable', 'boolean'],
19+
'public_show_question_answers' => ['nullable', 'boolean'],
20+
'public_show_order_details' => ['nullable', 'boolean'],
1821
];
1922
}
2023

0 commit comments

Comments
 (0)