Skip to content

Commit 70232f1

Browse files
andrew-polkhatton
authored andcommitted
Add People Reached stat (BL-11496)
1 parent 6df32bd commit 70232f1

8 files changed

Lines changed: 205 additions & 110 deletions

File tree

.vscode/settings.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
"phash",
2222
"pojo",
2323
"rebrand",
24-
"Rebrands",
25-
"Shellbook",
24+
"rebrands",
25+
"shellbook",
26+
"shellbooks",
2627
"subcollection",
2728
"uilang",
2829
"voca",

src/assets/Girl.svg

Lines changed: 10 additions & 0 deletions
Loading

src/components/statistics/StatsCard.tsx

Lines changed: 98 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const StatsCard: React.FunctionComponent<{
1616
overrideTotal?: number | React.FunctionComponent;
1717
subitems?: IItem[];
1818
info?: string;
19+
icon?: string;
1920
}> = (props) => {
2021
let totalAsCompactString = undefined;
2122
let totalAsFormattedString = undefined;
@@ -25,13 +26,35 @@ export const StatsCard: React.FunctionComponent<{
2526
totalAsFormattedString = props.overrideTotal.toLocaleString();
2627
} else if (props.overrideTotal === undefined) {
2728
const v = props.subitems
28-
?.map((i) => i.value)
29+
?.map((i) => (i.excludeFromTotal ? 0 : i.value))
2930
.reduce((t: number, i: number) => t + i, 0);
3031

3132
totalAsCompactString = formatNumber(v);
3233
totalAsFormattedString = v?.toLocaleString();
3334
} else overrideComponent = props.overrideTotal;
3435

36+
function getSubItemRow(rowNum: number): JSX.Element {
37+
const subItemsForRow = props.subitems
38+
?.filter((i) => {
39+
if (!i.row || i.row < 1) i.row = 1;
40+
return i.row === rowNum;
41+
})
42+
.map((i) => getSubItem(i));
43+
44+
return subItemsForRow?.length ? (
45+
<div
46+
css={css`
47+
display: flex;
48+
margin-top: ${rowNum === 1 ? "auto" : "10px"};
49+
`}
50+
>
51+
{subItemsForRow}
52+
</div>
53+
) : (
54+
<React.Fragment />
55+
);
56+
}
57+
3558
return (
3659
<Card
3760
key={props.children?.toString()}
@@ -50,7 +73,7 @@ export const StatsCard: React.FunctionComponent<{
5073
:last-child {
5174
padding-bottom: 16px;
5275
}
53-
height: 200px;
76+
height: 230px;
5477
5578
display: flex;
5679
flex-direction: column;
@@ -78,6 +101,7 @@ export const StatsCard: React.FunctionComponent<{
78101
</IconButton>
79102
</Tooltip>
80103
)}
104+
81105
<div
82106
css={css`
83107
&,
@@ -89,66 +113,96 @@ export const StatsCard: React.FunctionComponent<{
89113
>
90114
{props.children}
91115
</div>
92-
<div
93-
css={css`
94-
font-size: 48px;
95-
font-weight: bold;
96-
margin-bottom: 0;
97-
`}
98-
title={totalAsFormattedString}
99-
>
100-
{/* overrideTotal can be either a number or a function supplying a react component*/}
101-
{totalAsCompactString || overrideComponent!({})}
102-
</div>
103116
<div
104117
css={css`
105118
display: flex;
106-
flex-direction: row;
107-
margin-top: auto;
119+
justify-content: center;
120+
align-items: center;
108121
`}
109122
>
110-
{props.subitems?.map((i) => (
111-
// One sub item
123+
{props.icon && (
112124
<div
113-
key={i.label}
114125
css={css`
115-
margin-right: 10px;
116-
min-width: 60px;
117-
118-
&,
119-
* {
120-
text-align: left;
121-
}
126+
line-height: 0;
127+
height: 60px;
128+
width: 60px; // define its width
129+
margin-inline-end: 15px; // give it margin to give space between it and the total
130+
margin-inline-start: -75px; // now move it over the sum of the previous two so the total can stay centered
122131
`}
123132
>
124-
<div
133+
<img
134+
src={props.icon}
125135
css={css`
126-
color: ${kDarkGrey};
127-
font-size: 12px;
128-
129-
vertical-align: bottom;
130-
display: table-cell;
136+
height: 60px;
137+
width: 60px;
138+
object-fit: contain;
131139
`}
132-
>
133-
{i.label}
134-
</div>
135-
<div
136-
css={css`
137-
font-size: 24px;
138-
font-weight: bold;
139-
`}
140-
title={Intl.NumberFormat([]).format(i.value)}
141-
>
142-
{formatNumber(i.value)}
143-
</div>
140+
alt=""
141+
/>
144142
</div>
145-
))}
143+
)}
144+
<div
145+
css={css`
146+
font-size: 48px;
147+
font-weight: bold;
148+
margin-bottom: 0;
149+
`}
150+
title={totalAsFormattedString}
151+
>
152+
{/* overrideTotal can be either a number or a function supplying a react component*/}
153+
{totalAsCompactString || overrideComponent!({})}
154+
</div>
146155
</div>
156+
{getSubItemRow(1)}
157+
{getSubItemRow(2)}
147158
</CardContent>
148159
</Card>
149160
);
150161
};
151162

163+
function getSubItem(i: IItem): JSX.Element {
164+
return (
165+
<div
166+
key={i.label}
167+
css={css`
168+
margin-right: 20px;
169+
&:last-child {
170+
margin-right: 0;
171+
}
172+
173+
max-width: max-content;
174+
175+
&,
176+
* {
177+
text-align: left;
178+
}
179+
`}
180+
>
181+
<div
182+
css={css`
183+
color: ${kDarkGrey};
184+
font-size: 11px;
185+
186+
vertical-align: bottom;
187+
display: table-cell;
188+
`}
189+
>
190+
{i.label}
191+
</div>
192+
<div
193+
css={css`
194+
font-size: 24px;
195+
font-weight: bold;
196+
line-height: 1;
197+
`}
198+
title={Intl.NumberFormat([]).format(i.value)}
199+
>
200+
{formatNumber(i.value)}
201+
</div>
202+
</div>
203+
);
204+
}
205+
152206
function formatNumber(i: number | undefined): string {
153207
if (i === undefined) {
154208
return "0";

src/components/statistics/StatsInterfaces.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ export interface IOverviewStats {
2828
languages: number;
2929
topics: number;
3030

31-
bloomPubDeviceMobile: number;
32-
bloomPubDevicePC: number;
31+
usersWeb: number;
32+
usersApps: number;
33+
usersBloomReader: number;
34+
usersBloomPUBViewer: number;
3335

3436
downloadsEpub: number;
3537
downloadsBloomPub: number;
@@ -39,6 +41,8 @@ export interface IOverviewStats {
3941
readsWeb: number;
4042
readsApps: number;
4143
readsBloomReader: number;
44+
45+
countries: number;
4246
}
4347

4448
// These are query results in which each row represents one day.

src/components/statistics/StatsOverviewScreen.tsx

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ import { useGetOverviewStats } from "./useGetOverviewStats";
1111
import { FormattedMessage, useIntl } from "react-intl";
1212
import { BookCount } from "../BookCount";
1313

14-
//const gapWidth = "10px";
14+
// If we need more control over the icon in the future, use:
15+
//import { ReactComponent as PeopleReachedIcon } from "../assets/Girl.svg";
16+
// This lets us a do a simple <img src={icon} />:
17+
import peopleReachedIcon from "../../assets/Girl.svg";
18+
1519
export const kDarkGrey = "#5d5d5d";
1620

1721
export interface IItem {
1822
label: string;
1923
value: number;
24+
row?: number; //1-based
25+
excludeFromTotal?: boolean;
2026
}
2127
export const StatsOverviewScreen: React.FunctionComponent<IStatsPageProps> = (
2228
props
@@ -48,6 +54,58 @@ export const StatsOverviewScreen: React.FunctionComponent<IStatsPageProps> = (
4854
}
4955
`}
5056
>
57+
<StatsCard
58+
icon={peopleReachedIcon}
59+
info={i18n.formatMessage({
60+
id: "stats.people.reached.info",
61+
defaultMessage:
62+
"Will overcount when the same person reads one of these books via multiple devices or applications. Will undercount when firewalls or lack of connectivity permanently prevent analytics connections.",
63+
})}
64+
subitems={[
65+
{
66+
label: i18n.formatMessage({
67+
id: "stats.reads.web",
68+
defaultMessage: "Web",
69+
}),
70+
value: stats.usersWeb,
71+
},
72+
{
73+
label: i18n.formatMessage({
74+
id: "stats.reads.apps",
75+
defaultMessage: "Apps",
76+
}),
77+
value: stats.usersApps,
78+
},
79+
{
80+
label: i18n.formatMessage({
81+
id: "bloomReader",
82+
defaultMessage: "Bloom Reader",
83+
}),
84+
value: stats.usersBloomReader,
85+
},
86+
{
87+
label: i18n.formatMessage({
88+
id: "bloomPubViewer",
89+
defaultMessage: "BloomPUB Viewer",
90+
}),
91+
value: stats.usersBloomPUBViewer,
92+
},
93+
{
94+
label: i18n.formatMessage({
95+
id: "countries",
96+
defaultMessage: "Countries",
97+
}),
98+
value: stats.countries,
99+
row: 2,
100+
excludeFromTotal: true,
101+
},
102+
]}
103+
>
104+
<FormattedMessage
105+
id="stats.people.reached"
106+
defaultMessage="People Reached"
107+
/>
108+
</StatsCard>
51109
<StatsCard
52110
// I don't think we're ready for translation on this one yet...
53111
info={
@@ -86,43 +144,6 @@ export const StatsOverviewScreen: React.FunctionComponent<IStatsPageProps> = (
86144
<FormattedMessage id="books" defaultMessage="Books" />
87145
</StatsCard>
88146

89-
<StatsCard
90-
info={i18n.formatMessage({
91-
id: "stats.devices.info",
92-
defaultMessage:
93-
"Count of devices for which we received notice that at least one book from this collection had been read.",
94-
})}
95-
overrideTotal={stats.bloomPubDeviceMobile}
96-
// subitems={[
97-
// {
98-
// label: i18n.formatMessage({
99-
// id: "stats.devices.mobile",
100-
// defaultMessage: "Mobile",
101-
// }),
102-
// value: stats.bloomPubDeviceMobile,
103-
// },
104-
// {
105-
// label: i18n.formatMessage({
106-
// id: "stats.devices.pc",
107-
// defaultMessage: "PC",
108-
// }),
109-
// value: stats.bloomPubDevicePC,
110-
// },
111-
// ]}
112-
>
113-
<FormattedMessage id="devices" defaultMessage="Devices" />
114-
<div
115-
css={css`
116-
font-size: 12px;
117-
`}
118-
>
119-
<FormattedMessage
120-
id="stats.devices.bloomReader"
121-
defaultMessage="with Bloom Reader"
122-
/>
123-
</div>
124-
</StatsCard>
125-
126147
<StatsCard
127148
info={i18n.formatMessage({
128149
id: "stats.reads.info",

0 commit comments

Comments
 (0)