Skip to content

Commit

Permalink
Update Event dashboard UI
Browse files Browse the repository at this point in the history
  • Loading branch information
daveearley committed Sep 11, 2024
1 parent 620398d commit 65d436f
Show file tree
Hide file tree
Showing 18 changed files with 195 additions and 29 deletions.
13 changes: 13 additions & 0 deletions backend/app/DomainObjects/EventDomainObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class EventDomainObject extends Generated\EventDomainObjectAbstract implements I

private ?OrganizerDomainObject $organizer = null;

private ?EventStatisticDomainObject $eventStatistics = null;

public static function getAllowedFilterFields(): array
{
return [
Expand Down Expand Up @@ -246,4 +248,15 @@ public function setCapacityAssignments(?Collection $capacityAssignments): self

return $this;
}

public function getEventStatistics(): ?EventStatisticDomainObject
{
return $this->eventStatistics;
}

public function setEventStatistics(?EventStatisticDomainObject $eventStatistics): self
{
$this->eventStatistics = $eventStatistics;
return $this;
}
}
5 changes: 5 additions & 0 deletions backend/app/Models/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public function capacity_assignments(): HasMany
return $this->hasMany(CapacityAssignment::class);
}

public function event_statistics(): HasOne
{
return $this->hasOne(EventStatistic::class);
}

public static function boot()
{
parent::boot();
Expand Down
4 changes: 4 additions & 0 deletions backend/app/Resources/Event/EventResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ public function toArray(Request $request): array
!is_null($this->getOrganizer()),
fn() => new OrganizerResource($this->getOrganizer())
),
'statistics' => $this->when(
!is_null($this->getEventStatistics()),
fn() => new EventStatisticsResource($this->getEventStatistics())
),
];
}
}
27 changes: 27 additions & 0 deletions backend/app/Resources/Event/EventStatisticsResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace HiEvents\Resources\Event;

use HiEvents\DomainObjects\EventStatisticDomainObject;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin EventStatisticDomainObject
*/
class EventStatisticsResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'unique_views' => $this->getUniqueViews(),
'total_views' => $this->getTotalViews(),
'sales_total_gross' => $this->getSalesTotalGross(),
'total_tax' => $this->getTotalTax(),
'sales_total_before_additions' => $this->getSalesTotalBeforeAdditions(),
'total_fee' => $this->getTotalFee(),
'tickets_sold' => $this->getTicketsSold(),
'total_refunded' => $this->getTotalRefunded(),
];
}
}
2 changes: 2 additions & 0 deletions backend/app/Services/Handlers/Event/GetEventsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace HiEvents\Services\Handlers\Event;

use HiEvents\DomainObjects\EventSettingDomainObject;
use HiEvents\DomainObjects\EventStatisticDomainObject;
use HiEvents\DomainObjects\ImageDomainObject;
use HiEvents\DomainObjects\OrganizerDomainObject;
use HiEvents\Repository\Eloquent\Value\Relationship;
Expand All @@ -21,6 +22,7 @@ public function handle(GetEventsDTO $dto): LengthAwarePaginator
return $this->eventRepository
->loadRelation(new Relationship(ImageDomainObject::class))
->loadRelation(new Relationship(EventSettingDomainObject::class))
->loadRelation(new Relationship(EventStatisticDomainObject::class))
->loadRelation(new Relationship(
domainObject: OrganizerDomainObject::class,
name: 'organizer',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 89 additions & 4 deletions frontend/src/components/common/EventCard/EventCard.module.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,83 @@
@import "../../../styles/mixins.scss";

.card {
background-color: var(--mantine-color-body);
padding: 0 20px;
display: flex;
gap: 20px;
gap: 10px;
overflow: hidden;
padding: var(--mantine-spacing-sm) !important;

@include respond-below(sm) {
flex-direction: column;
gap: 0px;
}

&:hover .imageAndDate {
background-size: 210%;
}

.imageAndDate {
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
justify-content: center;
border-radius: 0;
width: 180px;
background-size: 150%;
background-position: center;
background-repeat: no-repeat;
border-radius: var(--tk-radius-md);
overflow: hidden;
transition: background-size 15s ease;

@include respond-below(sm) {
flex-direction: row;
gap: 20px;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
height: 100px;
}

.date {
font-size: var(--mantine-font-size-sm);
color: #fff;
font-weight: 500;
background-color: #320c51b5;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
flex-direction: column;

.day {
font-size: 2em;
font-weight: 500;
}

.month {
font-size: 1.5em;
font-weight: 500;
text-transform: uppercase;
}

.time {
font-size: 1em;
font-weight: 500;
}

@include respond-below(sm) {
.day {
font-size: 1.2em;
}
.month {
font-size: 0.8em;
}
}
}
}

.body {
padding: var(--mantine-spacing-xs);
flex: 1;
Expand Down Expand Up @@ -49,7 +116,7 @@

@include respond-below(sm) {
align-items: flex-start;
justify-content: flex-start;
justify-content: flex-start;
margin-bottom: var(--mantine-spacing-md);

.mobileButton {
Expand Down Expand Up @@ -83,5 +150,23 @@

.title {
line-height: 1.2;
margin-top: var(--tk-spacing-md);
margin-bottom: 4px;

a {
&:hover {
text-decoration: underline;
}
}
}

.organizer {
font-size: var(--mantine-font-size-sm);
margin-bottom: var(--tk-spacing-md);

a {
&:hover {
text-decoration: underline;
}
}
}
68 changes: 43 additions & 25 deletions frontend/src/components/common/EventCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import classes from "./EventCard.module.scss";
import {Card} from "../Card";
import {NavLink, useNavigate} from "react-router-dom";
import {
IconCalendarEvent,
IconArchive,
IconCopy,
IconDotsVertical,
IconEye,
IconMap,
IconQrcode,
IconSettings,
IconUser,
} from "@tabler/icons-react";
import {relativeDate} from "../../../utilites/dates.ts";
import {t} from "@lingui/macro"
import {eventHomepagePath} from "../../../utilites/urlHelper.ts";
import {EventStatusBadge} from "../EventStatusBadge";
Expand All @@ -24,6 +22,11 @@ import {ActionMenu, MenuItem} from '../ActionMenu/index.tsx';
import {confirmationDialog} from "../../../utilites/confirmationDialog.tsx";
import {showError, showSuccess} from "../../../utilites/notifications.tsx";
import {useUpdateEventStatus} from "../../../mutations/useUpdateEventStatus.ts";
import {formatCurrency} from "../../../utilites/currency.ts";
import {formatNumber} from "../../../utilites/helpers.ts";
import {formatDate} from "../../../utilites/dates.ts";

const NUMBER_OF_THUMBNAILS = 10;

interface EventCardProps {
event: Event;
Expand All @@ -35,6 +38,13 @@ export function EventCard({event}: EventCardProps) {
const [eventId, setEventId] = useState<IdParam>();
const statusToggleMutation = useUpdateEventStatus();

const eventThumbnailPath = ((eventId: number) => {
const result = (eventId % NUMBER_OF_THUMBNAILS);
const imageNumber = result === 0 ? NUMBER_OF_THUMBNAILS : Math.abs(result);

return '/images/event-thumbnails/event-thumb-%d.jpg'.replace('%d', String(imageNumber));
});

const handleDuplicate = (event: Event) => {
setEventId(() => event.id);
duplicateModal.open();
Expand Down Expand Up @@ -63,13 +73,32 @@ export function EventCard({event}: EventCardProps) {
return (
<>
<Card className={classes.card}>
<div className={classes.imageAndDate}
style={{backgroundImage: `url(${eventThumbnailPath(event.id as number)})`}}>
<div className={classes.date}>
<div className={classes.day}>
{formatDate(event.start_date, 'D', event.timezone)}
</div>
<div className={classes.month}>
{formatDate(event.start_date, 'MMM', event.timezone)}
</div>
<div className={classes.time}>
{formatDate(event.start_date, 'HH:mm', event.timezone)}
</div>
</div>
</div>
<div className={classes.body}>
{event && <EventStatusBadge event={event}/>}
<Text className={classes.title} mt="xs" mb="md">
<div className={classes.title}>
<NavLink to={`/manage/event/${event.id}`}>
{event.title}
</NavLink>
</Text>
</div>
<div className={classes.organizer}>
<NavLink to={`/manage/organizer/${event?.organizer?.id}`}>
{event?.organizer?.name}
</NavLink>
</div>
<div className={classes.eventInfo}>
{event.settings?.location_details?.venue_name && (
<Group gap="xs" wrap="nowrap">
Expand All @@ -80,27 +109,16 @@ export function EventCard({event}: EventCardProps) {
</Group>
)}
{event.settings?.is_online_event && (
<Group gap="xs" wrap="nowrap">
<IconMap color={'#ccc'}/>
<Text size="xs">
{t`Online event`}
</Text>
</Group>
)}
<Group gap="xs" wrap="nowrap">
<IconCalendarEvent color={'#ccc'}/>
<Text size="xs">
{relativeDate(event.start_date)}
{t`Online event`}
</Text>
</Group>
<Group gap="xs" wrap="nowrap">
<IconUser color={'#ccc'}/>
<Text size="xs">
<NavLink to={`/manage/organizer/${event?.organizer?.id}`}>
{event?.organizer?.name}
</NavLink>
</Text>
</Group>
)}
<Text size="xs">
{formatNumber(event?.statistics?.tickets_sold || 0)} {t`tickets sold`}
</Text>
<Text size="xs">
{formatCurrency(event?.statistics?.sales_total_gross || 0, event?.currency)} {t`gross sales`}
</Text>
</div>
</div>
<div className={classes.actions}>
Expand Down Expand Up @@ -133,7 +151,7 @@ export function EventCard({event}: EventCardProps) {
},
{
label: event?.status === 'ARCHIVED' ? t`Restore event` : t`Archive event`,
icon: <IconCopy size={14}/>,
icon: <IconArchive size={14}/>,
onClick: handleStatusToggle(event)
},
].filter(Boolean) as MenuItem[],
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ export interface Event extends EventBase {
timezone: string;
organizer_id?: IdParam;
location_details?: VenueAddress;
statistics?: EventStatistics;
}

export interface EventStatistics {
unique_views: number;
total_views: number;
sales_total_gross: number;
total_tax: number;
sales_total_before_additions: number;
total_fee: number;
tickets_sold: number;
total_refunded: number;
}

export interface EventDailyStats {
Expand Down

0 comments on commit 65d436f

Please sign in to comment.