Skip to content

Commit 8d09a76

Browse files
authored
Merge pull request #427 from BloomBooks/BL-10968LocationStats
BL-10968 Add location tables to stats
2 parents 2f741ad + 833655f commit 8d09a76

10 files changed

+379
-227
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"name": "blorg",
3-
"version": "0.1.0",
43
"private": true,
54
"engines": {
65
"node": "~16.13.1"
@@ -42,6 +41,7 @@
4241
"downshift": "^3.2.10",
4342
"file-saver": "^2.0.2",
4443
"firebase": "^9.6.1",
44+
"lodash": "^4.17.21",
4545
"markdown-to-jsx": "^6.11.4",
4646
"match-sorter": "^4.0.1",
4747
"mobx": "^6.0.4",
@@ -93,6 +93,7 @@
9393
"@types/file-saver": "^2.0.1",
9494
"@types/fs-extra": "^9.0.4",
9595
"@types/jest": "^25.1.2",
96+
"@types/lodash": "^4.14.178",
9697
"@types/markdown-to-jsx": "^6.11.2",
9798
"@types/match-sorter": "^4.0.0",
9899
"@types/node": "12.7.2",

src/components/statistics/BookStatsReport.tsx

+21-49
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useProvideDataForExport } from "../../export/exportData";
2020
import { CachedTablesContext } from "../../model/CacheProvider";
2121
import { getDisplayNamesFromLanguageCode } from "../../model/Language";
2222
import { useIntl } from "react-intl";
23+
import { StatsGridWrapper } from "./StatsGridWrapper";
2324

2425
export const BookStatsReport: React.FunctionComponent<IStatsProps> = (
2526
props
@@ -129,57 +130,28 @@ export const BookStatsReport: React.FunctionComponent<IStatsProps> = (
129130

130131
// Configure numeric sorts for the last two columns (so 453 is not less than 5)
131132
const [integratedSortingColumnExtensions] = useState([
132-
{ columnName: "finishedCount", compare: compareNumbers },
133-
{ columnName: "startedCount", compare: compareNumbers },
133+
{ columnName: "finishedCount" },
134+
{ columnName: "startedCount" },
134135
]);
135136

136-
const gotRows = stats && stats.length > 0;
137137
return (
138-
<div
139-
css={css`
140-
background-color: ${gotRows && "white"};
141-
thead.MuiTableHead-root * {
142-
line-height: 15px;
143-
vertical-align: top;
144-
}
145-
// make the table line up with the rest of the page
146-
// (but don't interfere with the space between columns)
147-
th:first-child,
148-
td:first-child {
149-
padding-left: 0 !important;
150-
}
151-
`}
152-
>
153-
{gotRows || <div>No data found</div>}
154-
{gotRows && (
155-
<Grid rows={stats!} columns={columns}>
156-
<SortingState
157-
defaultSorting={[
158-
{ columnName: "title", direction: "asc" },
159-
]}
160-
/>
161-
<IntegratedSorting
162-
columnExtensions={integratedSortingColumnExtensions}
163-
/>
164-
<Table
165-
columnExtensions={tableColumnExtensions}
166-
//cellComponent={CustomTableCell}
167-
/>
168-
<TableHeaderRow
169-
cellComponent={CustomTableHeaderCell}
170-
showSortingControls
171-
/>
172-
</Grid>
173-
)}
174-
</div>
138+
<StatsGridWrapper stats={stats}>
139+
<Grid rows={stats!} columns={columns}>
140+
<SortingState
141+
defaultSorting={[{ columnName: "title", direction: "asc" }]}
142+
/>
143+
<IntegratedSorting
144+
columnExtensions={integratedSortingColumnExtensions}
145+
/>
146+
<Table
147+
columnExtensions={tableColumnExtensions}
148+
//cellComponent={CustomTableCell}
149+
/>
150+
<TableHeaderRow
151+
cellComponent={CustomTableHeaderCell}
152+
showSortingControls
153+
/>
154+
</Grid>
155+
</StatsGridWrapper>
175156
);
176157
};
177-
178-
const compareNumbers = (a: string, b: string): number => {
179-
const numA = parseInt(a);
180-
const numB = parseInt(b);
181-
if (numA === numB) {
182-
return 0;
183-
}
184-
return numA < numB ? -1 : 1;
185-
};

src/components/statistics/CollectionStatsPage.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { useGetLocalizedCollectionLabel } from "../../localization/CollectionLab
3737
import { ICollectionStatsPageProps } from "./CollectionStatsPageCodeSplit";
3838
import { PageNotFound } from "../PageNotFound";
3939
import { StatsCredits } from "./StatsCredits";
40+
import { StatsLocationScreen } from "./StatsLocationScreen";
4041

4142
export const Pretend: React.FunctionComponent<IStatsProps> = (props) => {
4243
return <h1>Pretend</h1>;
@@ -93,6 +94,15 @@ export const CollectionStatsPage: React.FunctionComponent<ICollectionStatsPagePr
9394
<BookStatsReport {...p}></BookStatsReport>
9495
),
9596
},
97+
{
98+
label: l10n.formatMessage({
99+
id: "stats.locations",
100+
defaultMessage: "Locations",
101+
}),
102+
component: (p: IStatsProps) => (
103+
<StatsLocationScreen {...p}></StatsLocationScreen>
104+
),
105+
},
96106
].sort((a, b) => a.label.localeCompare(b.label));
97107
// But keep the overview at the top, outside of the sort order
98108
x.unshift({
@@ -190,7 +200,7 @@ export const CollectionStatsPage: React.FunctionComponent<ICollectionStatsPagePr
190200
css={css`
191201
padding-left: 0;
192202
min-width: 300px;
193-
203+
background-color: white;
194204
select {
195205
padding: 10px !important;
196206
}

src/components/statistics/ComprehensionQuestionsReport.tsx

+32-74
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { IStatsProps } from "./StatsInterfaces";
1919
import { useGetBookComprehensionEventStats } from "./useGetBookStats";
2020
import { useProvideDataForExport } from "../../export/exportData";
2121
import { useIntl } from "react-intl";
22+
import { StatsGridWrapper } from "./StatsGridWrapper";
2223

2324
export const ComprehensionQuestionsReport: React.FunctionComponent<IStatsProps> = (
2425
props
@@ -63,10 +64,10 @@ export const ComprehensionQuestionsReport: React.FunctionComponent<IStatsProps>
6364

6465
// Configure numeric sorts for the last two columns (so 453 is not less than 5)
6566
const [integratedSortingColumnExtensions] = useState([
66-
{ columnName: "questions", compare: compareNumbers },
67-
{ columnName: "quizzesTaken", compare: compareNumbers },
68-
{ columnName: "meanCorrect", compare: compareNumbers },
69-
{ columnName: "medianCorrect", compare: compareNumbers },
67+
{ columnName: "questions" },
68+
{ columnName: "quizzesTaken" },
69+
{ columnName: "meanCorrect" },
70+
{ columnName: "medianCorrect" },
7071
]);
7172

7273
const CustomTableHeaderCell = (cellProps: any) => {
@@ -121,81 +122,38 @@ export const ComprehensionQuestionsReport: React.FunctionComponent<IStatsProps>
121122
}
122123
};
123124

124-
const gotRows = stats && stats.length > 0;
125-
126125
// const [headerColumnExtensions] = useState([
127126
// { columnName: "quizzesTaken", wordWrapEnabled: true },
128127
// { columnName: "meanCorrect", wordWrapEnabled: true },
129128
// { columnName: "medianCorrect", wordWrapEnabled: true },
130129
// ]);
131130
return (
132-
<div
133-
css={css`
134-
background-color: ${gotRows && "white"};
135-
thead.MuiTableHead-root * {
136-
line-height: 15px;
137-
vertical-align: top;
138-
}
139-
// make the table line up with the rest of the page
140-
// (but don't interfere with the space between columns)
141-
th:first-child,
142-
td:first-child {
143-
padding-left: 0 !important;
144-
}
145-
`}
146-
>
147-
{gotRows || <div>No data found</div>}
148-
{gotRows && (
149-
<Grid rows={stats!} columns={columns}>
150-
<SortingState
151-
defaultSorting={[
152-
{ columnName: "quizzesTaken", direction: "desc" },
153-
]}
154-
/>
155-
<IntegratedSorting
156-
columnExtensions={integratedSortingColumnExtensions}
157-
/>
158-
<Table
159-
columnExtensions={tableColumnExtensions}
160-
cellComponent={CustomTableCell}
161-
/>
162-
<TableColumnResizing
163-
resizingMode={"nextColumn"}
164-
defaultColumnWidths={columns.map((c) => ({
165-
columnName: c.name,
166-
width: "auto",
167-
}))}
168-
/>
169-
<TableHeaderRow
170-
cellComponent={CustomTableHeaderCell}
171-
showSortingControls
172-
/>
173-
</Grid>
174-
)}
175-
</div>
131+
<StatsGridWrapper stats={stats}>
132+
<Grid rows={stats!} columns={columns}>
133+
<SortingState
134+
defaultSorting={[
135+
{ columnName: "quizzesTaken", direction: "desc" },
136+
]}
137+
/>
138+
<IntegratedSorting
139+
columnExtensions={integratedSortingColumnExtensions}
140+
/>
141+
<Table
142+
columnExtensions={tableColumnExtensions}
143+
cellComponent={CustomTableCell}
144+
/>
145+
<TableColumnResizing
146+
resizingMode={"nextColumn"}
147+
defaultColumnWidths={columns.map((c) => ({
148+
columnName: c.name,
149+
width: "auto",
150+
}))}
151+
/>
152+
<TableHeaderRow
153+
cellComponent={CustomTableHeaderCell}
154+
showSortingControls
155+
/>
156+
</Grid>
157+
</StatsGridWrapper>
176158
);
177159
};
178-
179-
const compareNumbers = (
180-
a: string | undefined | null,
181-
b: string | undefined | null
182-
): number => {
183-
// First check for falsy strings. These are problematic.
184-
// If you don't handle them, the list will become sorted in arbitrary order.
185-
// We'll just define nulls as being worse than 0... so... negative infinity.
186-
let numA = a ? parseFloat(a) : Number.NEGATIVE_INFINITY;
187-
if (isNaN(numA)) {
188-
// Parse errors are also problematic.
189-
numA = Number.NEGATIVE_INFINITY;
190-
}
191-
192-
let numB = b ? parseFloat(b) : Number.NEGATIVE_INFINITY;
193-
if (isNaN(numB)) {
194-
numB = Number.NEGATIVE_INFINITY;
195-
}
196-
197-
if (numA === numB) {
198-
return 0;
199-
}
200-
return numA < numB ? -1 : 1;
201-
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// this engages a babel macro that does cool emotion stuff (like source maps). See https://emotion.sh/docs/babel-macros
2+
import css from "@emotion/css/macro";
3+
// these two lines make the css prop work on react elements
4+
import { jsx } from "@emotion/core";
5+
/** @jsx jsx */
6+
import React from "react";
7+
8+
export const StatsGridWrapper: React.FunctionComponent<{
9+
stats?: Array<any>;
10+
}> = (props) => {
11+
const gotRows = !!props.stats && props.stats.length > 0;
12+
return (
13+
<div
14+
css={css`
15+
background-color: ${gotRows && "white"};
16+
thead.MuiTableHead-root * {
17+
line-height: 15px;
18+
vertical-align: top;
19+
}
20+
// make the table line up with the rest of the page
21+
// (but don't interfere with the space between columns)
22+
th:first-child,
23+
td:first-child {
24+
padding-left: 0 !important;
25+
}
26+
padding: 10px;
27+
`}
28+
>
29+
{gotRows || <div>No data found</div>}
30+
{gotRows && props.children}
31+
</div>
32+
);
33+
};

0 commit comments

Comments
 (0)