Skip to content

Commit f71d42f

Browse files
committed
update promo code report
1 parent 2dc6202 commit f71d42f

File tree

7 files changed

+225
-92
lines changed

7 files changed

+225
-92
lines changed

backend/app/Resources/Product/ProductResourcePublic.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public function toArray(Request $request): array
1818
'id' => $this->getId(),
1919
'title' => $this->getTitle(),
2020
'type' => $this->getType(),
21+
'product_type' => $this->getProductType(),
2122
'description' => $this->getDescription(),
2223
'max_per_order' => $this->getMaxPerOrder(),
2324
'min_per_order' => $this->getMinPerOrder(),

backend/app/Services/Domain/Report/Reports/PromoCodesReport.php

Lines changed: 93 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13,79 +13,100 @@ protected function getSqlQuery(Carbon $startDate, Carbon $endDate): string
1313
$endDateString = $endDate->format('Y-m-d H:i:s');
1414

1515
return <<<SQL
16-
WITH promo_metrics AS (
17-
SELECT
18-
COALESCE(pc.code, o.promo_code) as promo_code,
19-
COUNT(DISTINCT o.id) as times_used,
20-
COUNT(DISTINCT o.email) as unique_customers,
21-
COALESCE(SUM(o.total_gross), 0) as total_gross_sales,
22-
COALESCE(SUM(o.total_before_additions), 0) as total_before_discounts,
23-
COALESCE(SUM(o.total_before_additions - o.total_gross), 0) as total_discount_amount,
24-
CASE
25-
WHEN COUNT(o.id) > 0 THEN ROUND(AVG(o.total_before_additions - o.total_gross)::numeric, 2)
26-
ELSE 0
27-
END as avg_discount_per_order,
28-
CASE
29-
WHEN COUNT(o.id) > 0 THEN ROUND(AVG(o.total_gross)::numeric, 2)
30-
ELSE 0
31-
END as avg_order_value,
32-
MIN(o.created_at AT TIME ZONE 'UTC') as first_used_at,
33-
MAX(o.created_at AT TIME ZONE 'UTC') as last_used_at,
34-
pc.discount as configured_discount,
35-
pc.discount_type,
36-
pc.max_allowed_usages,
37-
pc.expiry_date AT TIME ZONE 'UTC' as expiry_date,
38-
CASE
39-
WHEN pc.max_allowed_usages IS NOT NULL
40-
THEN pc.max_allowed_usages - COUNT(o.id)::integer
41-
END as remaining_uses,
42-
CASE
43-
WHEN pc.expiry_date < CURRENT_TIMESTAMP THEN 'Expired'
44-
WHEN pc.max_allowed_usages IS NOT NULL AND COUNT(o.id) >= pc.max_allowed_usages THEN 'Limit Reached'
45-
WHEN pc.deleted_at IS NOT NULL THEN 'Deleted'
46-
ELSE 'Active'
47-
END as status
48-
FROM promo_codes pc
49-
LEFT JOIN orders o ON
50-
pc.id = o.promo_code_id
51-
AND o.deleted_at IS NULL
52-
AND o.status NOT IN ('RESERVED')
53-
AND o.event_id = :event_id
54-
AND o.created_at >= '$startDateString'
55-
AND o.created_at <= '$endDateString'
56-
WHERE
57-
pc.deleted_at IS NULL
58-
AND pc.event_id = :event_id
59-
GROUP BY
60-
pc.id,
61-
COALESCE(pc.code, o.promo_code),
62-
pc.discount,
63-
pc.discount_type,
64-
pc.max_allowed_usages,
65-
pc.expiry_date,
66-
pc.deleted_at
67-
)
16+
WITH order_totals AS (
6817
SELECT
69-
promo_code,
70-
times_used,
71-
unique_customers,
72-
configured_discount,
73-
discount_type,
74-
total_gross_sales,
75-
total_before_discounts,
76-
total_discount_amount,
77-
avg_discount_per_order,
78-
avg_order_value,
79-
first_used_at,
80-
last_used_at,
81-
max_allowed_usages,
82-
remaining_uses,
83-
expiry_date,
84-
status
85-
FROM promo_metrics
86-
ORDER BY
87-
total_gross_sales DESC,
88-
promo_code;
18+
o.id as order_id,
19+
o.promo_code_id,
20+
o.promo_code,
21+
SUM(oi.price * oi.quantity) as original_total,
22+
SUM(oi.price_before_discount * oi.quantity) as discounted_total,
23+
o.total_gross,
24+
o.email,
25+
o.created_at
26+
FROM orders o
27+
JOIN order_items oi ON oi.order_id = o.id
28+
WHERE
29+
o.deleted_at IS NULL
30+
AND o.status NOT IN ('RESERVED')
31+
AND o.event_id = :event_id
32+
AND o.created_at >= '$startDateString'
33+
AND o.created_at <= '$endDateString'
34+
35+
GROUP BY
36+
o.id,
37+
o.promo_code_id,
38+
o.promo_code,
39+
o.total_gross,
40+
o.email,
41+
o.created_at
42+
),
43+
promo_metrics AS (
44+
SELECT
45+
COALESCE(pc.code, ot.promo_code) as promo_code,
46+
COUNT(DISTINCT ot.order_id) as times_used,
47+
COUNT(DISTINCT ot.email) as unique_customers,
48+
COALESCE(SUM(ot.total_gross), 0) as total_gross_sales,
49+
COALESCE(SUM(ot.original_total), 0) as total_before_discounts,
50+
COALESCE(SUM(ot.original_total - ot.discounted_total), 0) as total_discount_amount,
51+
CASE
52+
WHEN COUNT(ot.order_id) > 0 THEN ROUND(AVG(ot.original_total - ot.discounted_total)::numeric, 2)
53+
ELSE 0
54+
END as avg_discount_per_order,
55+
CASE
56+
WHEN COUNT(ot.order_id) > 0 THEN ROUND(AVG(ot.total_gross)::numeric, 2)
57+
ELSE 0
58+
END as avg_order_value,
59+
MIN(ot.created_at AT TIME ZONE 'UTC') as first_used_at,
60+
MAX(ot.created_at AT TIME ZONE 'UTC') as last_used_at,
61+
pc.discount as configured_discount,
62+
pc.discount_type,
63+
pc.max_allowed_usages,
64+
pc.expiry_date AT TIME ZONE 'UTC' as expiry_date,
65+
CASE
66+
WHEN pc.max_allowed_usages IS NOT NULL
67+
THEN pc.max_allowed_usages - COUNT(ot.order_id)::integer
68+
END as remaining_uses,
69+
CASE
70+
WHEN pc.expiry_date < CURRENT_TIMESTAMP THEN 'Expired'
71+
WHEN pc.max_allowed_usages IS NOT NULL AND COUNT(ot.order_id) >= pc.max_allowed_usages THEN 'Limit Reached'
72+
WHEN pc.deleted_at IS NOT NULL THEN 'Deleted'
73+
ELSE 'Active'
74+
END as status
75+
FROM promo_codes pc
76+
LEFT JOIN order_totals ot ON pc.id = ot.promo_code_id
77+
WHERE
78+
pc.deleted_at IS NULL
79+
AND pc.event_id = :event_id
80+
GROUP BY
81+
pc.id,
82+
COALESCE(pc.code, ot.promo_code),
83+
pc.discount,
84+
pc.discount_type,
85+
pc.max_allowed_usages,
86+
pc.expiry_date,
87+
pc.deleted_at
88+
)
89+
SELECT
90+
promo_code,
91+
times_used,
92+
unique_customers,
93+
configured_discount,
94+
discount_type,
95+
total_gross_sales,
96+
total_before_discounts,
97+
total_discount_amount,
98+
avg_discount_per_order,
99+
avg_order_value,
100+
first_used_at,
101+
last_used_at,
102+
max_allowed_usages,
103+
remaining_uses,
104+
expiry_date,
105+
status
106+
FROM promo_metrics
107+
ORDER BY
108+
total_gross_sales DESC,
109+
promo_code;
89110
SQL;
90111
}
91112
}

docker/development/.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ APP_DEBUG=true
55

66
API_URL_CLIENT=https://localhost:8443/api
77
API_URL_SERVER=http://backend:80
8-
STRIPE_PUBLIC_KEY=pk_test_51J3J9vJ9J9vJ9
8+
STRIPE_PUBLIC_KEY=pk_test_51Ofu1CJKnXOyGeQuDPUHiZcJxZozRuERiv4vQRBtCscwTbxOL574cxUjAoNRL2YLCumgC5160pl6kvTIiAc9mOeM0058KAWQ55
99
FRONTEND_URL=https://localhost:8443
1010

1111
DB_CONNECTION=pgsql
1212
DB_HOST=pgsql
1313
DB_PORT=5432
1414
DB_DATABASE=backend
1515
DB_USERNAME=username
16-
DB_PASSWORD=password
16+
DB_PASSWORD=password
Lines changed: 62 additions & 0 deletions
Loading
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
@import "../../../styles/mixins.scss";
2+
3+
.periodSelect, .datePicker, .downloadButton {
4+
@include respond-below(sm) {
5+
width: 100%;
6+
}
7+
}

frontend/src/components/common/ReportTable/index.tsx

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ComboboxItem, Group, LoadingOverlay, Paper, Select, Table as MantineTable} from '@mantine/core';
1+
import {ComboboxItem, Group, Select, Skeleton, Table as MantineTable} from '@mantine/core';
22
import {t} from '@lingui/macro';
33
import {DatePickerInput} from "@mantine/dates";
44
import {IconArrowDown, IconArrowsSort, IconArrowUp, IconCalendar} from "@tabler/icons-react";
@@ -13,6 +13,8 @@ import {Event} from "../../../types.ts";
1313
import dayjs from 'dayjs';
1414
import utc from 'dayjs/plugin/utc';
1515
import timezone from 'dayjs/plugin/timezone';
16+
import {NoResultsSplash} from "../NoResultsSplash";
17+
import classes from './ReportTable.module.scss';
1618

1719
dayjs.extend(utc);
1820
dayjs.extend(timezone);
@@ -54,7 +56,6 @@ const TIME_PERIODS = [
5456
const ReportTable = <T extends Record<string, any>>({
5557
title,
5658
columns,
57-
isLoading = false,
5859
showDateFilter = true,
5960
defaultStartDate = new Date(new Date().setMonth(new Date().getMonth() - 3)),
6061
defaultEndDate = new Date(),
@@ -145,14 +146,6 @@ const ReportTable = <T extends Record<string, any>>({
145146
onDateRangeChange?.(tzRange);
146147
};
147148

148-
if (isLoading) {
149-
return (
150-
<Paper p="md" pos="relative">
151-
<LoadingOverlay visible={true}/>
152-
</Paper>
153-
);
154-
}
155-
156149
const handleSort = (field: keyof T) => {
157150
if (sortField === field) {
158151
if (sortDirection === 'asc') setSortDirection('desc');
@@ -226,6 +219,37 @@ const ReportTable = <T extends Record<string, any>>({
226219
}
227220
};
228221

222+
if (reportQuery.isLoading) {
223+
return (
224+
<>
225+
<Group justify="space-between" mb="xl">
226+
<Skeleton height={32} width={200}/>
227+
<Group gap="sm">
228+
<Skeleton height={32} width={200}/>
229+
<Skeleton height={32} width={130}/>
230+
</Group>
231+
</Group>
232+
<Skeleton height={300} radius="md"/>
233+
</>
234+
);
235+
}
236+
237+
if (reportQuery.isFetched && !reportQuery.isLoading && !data.length) {
238+
return (
239+
<NoResultsSplash
240+
heading={t`Nothing to show yet`}
241+
imageHref={'/blank-slate/reports.svg'}
242+
subHeading={(
243+
<>
244+
<p>
245+
{t`Once you start collecting data, you'll see it here.`}
246+
</p>
247+
</>
248+
)}
249+
/>
250+
);
251+
}
252+
229253
return (
230254
<>
231255
<Group justify="space-between" mb="md">
@@ -240,6 +264,7 @@ const ReportTable = <T extends Record<string, any>>({
240264
onChange={handlePeriodChange}
241265
leftSection={<IconCalendar stroke={1.5} size={20}/>}
242266
mb="0"
267+
className={classes.periodSelect}
243268
/>
244269
)}
245270
{showDateFilter && showDatePickerInput && (
@@ -252,14 +277,15 @@ const ReportTable = <T extends Record<string, any>>({
252277
onChange={handleDateRangeChange}
253278
minDate={dayjs().subtract(1, 'year').tz(event.timezone).toDate()}
254279
maxDate={dayjs().tz(event.timezone).toDate()}
255-
tim
280+
className={classes.datePicker}
256281
/>
257282
)}
258283
{enableDownload && (
259284
<DownloadCsvButton
260285
headers={csvHeaders}
261286
data={csvData}
262287
filename={downloadFileName}
288+
className={classes.downloadButton}
263289
/>
264290
)}
265291
</Group>

0 commit comments

Comments
 (0)