diff --git a/backend/app/Exports/AttendeesExport.php b/backend/app/Exports/AttendeesExport.php index 152ec72f..7f5d0d42 100644 --- a/backend/app/Exports/AttendeesExport.php +++ b/backend/app/Exports/AttendeesExport.php @@ -3,8 +3,12 @@ namespace HiEvents\Exports; use Carbon\Carbon; +use HiEvents\DomainObjects\AttendeeDomainObject; use HiEvents\DomainObjects\Enums\QuestionTypeEnum; +use HiEvents\DomainObjects\Enums\TicketType; use HiEvents\DomainObjects\QuestionDomainObject; +use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\TicketPriceDomainObject; use HiEvents\Resources\Attendee\AttendeeResource; use HiEvents\Services\Domain\Question\QuestionAnswerFormatter; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -50,6 +54,7 @@ public function headings(): array 'Is Checked In', 'Checked In At', 'Ticket ID', + 'Ticket Name', 'Event ID', 'Public ID', 'Short ID', @@ -58,6 +63,10 @@ public function headings(): array ], $questionTitles); } + /** + * @param AttendeeDomainObject $attendee + * @return array + */ public function map($attendee): array { $answers = $this->questions->map(function (QuestionDomainObject $question) use ($attendee) { @@ -70,17 +79,28 @@ public function map($attendee): array ); }); + /** @var TicketDomainObject $ticket */ + $ticket = $attendee->getTicket(); + $ticketName = $ticket->getTitle(); + if ($attendee->getTicket()?->getType() === TicketType::TIERED->name) { + $ticketName .= ' - ' . $ticket + ->getTicketPrices() + ->first(fn(TicketPriceDomainObject $tp) => $tp->getId() === $attendee->getTicketPriceId()) + ->getLabel(); + } + return array_merge([ $attendee->getId(), $attendee->getFirstName(), $attendee->getLastName(), $attendee->getEmail(), $attendee->getStatus(), - $attendee->getCheckedInAt() ? 'Yes' : 'No', - $attendee->getCheckedInAt() - ? Carbon::parse($attendee->getCheckedInAt())->format('Y-m-d H:i:s') + $attendee->getCheckIn() ? 'Yes' : 'No', + $attendee->getCheckIn() + ? Carbon::parse($attendee->getCheckIn()->getCreatedAt())->format('Y-m-d H:i:s') : '', $attendee->getTicketId(), + $ticketName, $attendee->getEventId(), $attendee->getPublicId(), $attendee->getShortId(), diff --git a/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php b/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php index 3d36e38c..729b5c8a 100644 --- a/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php +++ b/backend/app/Http/Actions/Attendees/ExportAttendeesAction.php @@ -2,11 +2,15 @@ namespace HiEvents\Http\Actions\Attendees; +use HiEvents\DomainObjects\AttendeeCheckInDomainObject; use HiEvents\DomainObjects\Enums\QuestionBelongsTo; use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\QuestionAndAnswerViewDomainObject; +use HiEvents\DomainObjects\TicketDomainObject; +use HiEvents\DomainObjects\TicketPriceDomainObject; use HiEvents\Exports\AttendeesExport; use HiEvents\Http\Actions\BaseAction; +use HiEvents\Repository\Eloquent\Value\Relationship; use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface; use HiEvents\Repository\Interfaces\QuestionRepositoryInterface; use Maatwebsite\Excel\Facades\Excel; @@ -31,6 +35,19 @@ public function __invoke(int $eventId): BinaryFileResponse $attendees = $this->attendeeRepository ->loadRelation(QuestionAndAnswerViewDomainObject::class) + ->loadRelation(new Relationship( + domainObject: AttendeeCheckInDomainObject::class, + name: 'check_in', + )) + ->loadRelation(new Relationship( + domainObject: TicketDomainObject::class, + nested: [ + new Relationship( + domainObject: TicketPriceDomainObject::class, + ), + ], + name: 'ticket' + )) ->findByEventIdForExport($eventId); $questions = $this->questionRepository->findWhere([ diff --git a/backend/app/Http/Actions/Attendees/GetAttendeeAction.php b/backend/app/Http/Actions/Attendees/GetAttendeeAction.php index 6d82bbbd..f022a734 100644 --- a/backend/app/Http/Actions/Attendees/GetAttendeeAction.php +++ b/backend/app/Http/Actions/Attendees/GetAttendeeAction.php @@ -2,6 +2,7 @@ namespace HiEvents\Http\Actions\Attendees; +use HiEvents\DomainObjects\AttendeeCheckInDomainObject; use HiEvents\DomainObjects\EventDomainObject; use HiEvents\DomainObjects\QuestionAndAnswerViewDomainObject; use HiEvents\DomainObjects\TicketDomainObject; @@ -27,7 +28,7 @@ public function __invoke(int $eventId, int $attendeeId): Response|JsonResponse $this->isActionAuthorized($eventId, EventDomainObject::class); $attendee = $this->attendeeRepository - ->loadRelation(QuestionAndAnswerViewDomainObject::class) + ->loadRelation(relationship: QuestionAndAnswerViewDomainObject::class) ->loadRelation(new Relationship( domainObject: TicketDomainObject::class, nested: [ @@ -35,6 +36,10 @@ public function __invoke(int $eventId, int $attendeeId): Response|JsonResponse domainObject: TicketPriceDomainObject::class, ), ], name: 'ticket')) + ->loadRelation(new Relationship( + domainObject: AttendeeCheckInDomainObject::class, + name: 'check_in', + )) ->findFirstWhere([ 'id' => $attendeeId, 'event_id' => $eventId, diff --git a/backend/app/Repository/Eloquent/CheckInListRepository.php b/backend/app/Repository/Eloquent/CheckInListRepository.php index 45d80446..e539b130 100644 --- a/backend/app/Repository/Eloquent/CheckInListRepository.php +++ b/backend/app/Repository/Eloquent/CheckInListRepository.php @@ -41,6 +41,7 @@ public function getCheckedInAttendeeCountById(int $checkInListId): CheckedInAtte JOIN ticket_check_in_lists tcil ON a.ticket_id = tcil.ticket_id WHERE a.deleted_at IS NULL AND tcil.deleted_at IS NULL + AND a.status = 'ACTIVE' ) SELECT cil.id AS check_in_list_id, diff --git a/backend/app/Resources/Attendee/AttendeeResource.php b/backend/app/Resources/Attendee/AttendeeResource.php index 77a7f924..819ea89a 100644 --- a/backend/app/Resources/Attendee/AttendeeResource.php +++ b/backend/app/Resources/Attendee/AttendeeResource.php @@ -4,6 +4,7 @@ use HiEvents\DomainObjects\AttendeeDomainObject; use HiEvents\DomainObjects\Enums\QuestionBelongsTo; +use HiEvents\Resources\CheckInList\AttendeeCheckInResource; use HiEvents\Resources\Order\OrderResource; use HiEvents\Resources\Question\QuestionAnswerViewResource; use HiEvents\Resources\Ticket\TicketResource; @@ -30,17 +31,21 @@ public function toArray(Request $request): array 'public_id' => $this->getPublicId(), 'short_id' => $this->getShortId(), 'locale' => $this->getLocale(), + 'check_in' => $this->when( + condition: $this->getCheckIn() !== null, + value: fn() => new AttendeeCheckInResource($this->getCheckIn()), + ), 'ticket' => $this->when( - !is_null($this->getTicket()), - fn() => new TicketResource($this->getTicket()), + condition: !is_null($this->getTicket()), + value: fn() => new TicketResource($this->getTicket()), ), 'order' => $this->when( - !is_null($this->getOrder()), - fn() => new OrderResource($this->getOrder()) + condition: !is_null($this->getOrder()), + value: fn() => new OrderResource($this->getOrder()) ), 'question_answers' => $this->when( - $this->getQuestionAndAnswerViews() !== null, - fn() => QuestionAnswerViewResource::collection( + condition: $this->getQuestionAndAnswerViews() !== null, + value: fn() => QuestionAnswerViewResource::collection( $this->getQuestionAndAnswerViews() ?->filter(fn($qav) => $qav->getBelongsTo() === QuestionBelongsTo::TICKET->name) ) diff --git a/backend/app/Resources/CheckInList/AttendeeCheckInResource.php b/backend/app/Resources/CheckInList/AttendeeCheckInResource.php new file mode 100644 index 00000000..15246015 --- /dev/null +++ b/backend/app/Resources/CheckInList/AttendeeCheckInResource.php @@ -0,0 +1,25 @@ + $this->getId(), + 'attendee_id' => $this->getAttendeeId(), + 'check_in_list_id' => $this->getCheckInListId(), + 'ticket_id' => $this->getTicketId(), + 'event_id' => $this->getEventId(), + 'short_id' => $this->getShortId(), + 'created_at' => $this->getCreatedAt(), + ]; + } +} diff --git a/frontend/src/api/check-in.client.ts b/frontend/src/api/check-in.client.ts index d932e52a..98765ea4 100644 --- a/frontend/src/api/check-in.client.ts +++ b/frontend/src/api/check-in.client.ts @@ -1,11 +1,10 @@ import {publicApi} from "./public-client"; import { Attendee, - CheckIn, CheckInList, GenericDataResponse, GenericPaginatedResponse, - IdParam, + IdParam, PublicCheckIn, QueryFilters, } from "../types"; import {queryParamsHelper} from "../utilites/queryParamsHelper"; @@ -20,13 +19,13 @@ export const publicCheckInClient = { return response.data; }, createCheckIn: async (checkInListShortId: IdParam, attendeePublicId: IdParam) => { - const response = await publicApi.post>(`/check-in-lists/${checkInListShortId}/check-ins`, { + const response = await publicApi.post>(`/check-in-lists/${checkInListShortId}/check-ins`, { "attendee_public_ids": [attendeePublicId], }); return response.data; }, deleteCheckIn: async (checkInListShortId: IdParam, checkInShortId: IdParam) => { - const response = await publicApi.delete>(`/check-in-lists/${checkInListShortId}/check-ins/${checkInShortId}`); + const response = await publicApi.delete>(`/check-in-lists/${checkInListShortId}/check-ins/${checkInShortId}`); return response.data; }, }; diff --git a/frontend/src/components/common/AttendeeDetails/index.tsx b/frontend/src/components/common/AttendeeDetails/index.tsx index b15acff7..414bca71 100644 --- a/frontend/src/components/common/AttendeeDetails/index.tsx +++ b/frontend/src/components/common/AttendeeDetails/index.tsx @@ -38,7 +38,7 @@ export const AttendeeDetails = ({attendee}: { attendee: Attendee }) => { {t`Checked In`}
- {attendee.checked_in_at ? t`Yes` : t`No`} + {attendee.check_in ? t`Yes` : t`No`}
@@ -59,4 +59,4 @@ export const AttendeeDetails = ({attendee}: { attendee: Attendee }) => {
); -} \ No newline at end of file +} diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 771fe42b..82e8b881 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -345,7 +345,19 @@ export interface Attendee { checked_in_by?: number; question_answers?: QuestionAnswer[]; locale?: SupportedLocales; - check_in?: CheckIn; + check_in?: AttendeeCheckIn; +} + +export type PublicCheckIn = Pick; + +export interface AttendeeCheckIn { + id: IdParam; + attendee_id: IdParam; + check_in_list_id: IdParam; + ticket_id: IdParam; + event_id: IdParam; + short_id: IdParam; + created_at: string; } export interface Address { @@ -475,14 +487,6 @@ export type CheckInListRequest = ticket_ids: IdParam[]; }; -export interface CheckIn { - id: number; - short_id: string; - check_in_list_id: number; - attendee_id: number; - checked_in_at: string; -} - export interface QuestionRequestData { title: string; description?: string;