Add insights page

This commit is contained in:
Brandon Presley 2023-10-24 21:32:31 +13:00
parent b1d77cbdce
commit f61109cea3
6 changed files with 276 additions and 19 deletions

56
AppBarChart.tsx Normal file
View File

@ -0,0 +1,56 @@
import { useWindowDimensions } from "react-native";
import { PieChart } from "react-native-chart-kit";
export interface Option {
value: number;
label: string;
}
export default function AppBarChart({ options }: { options: Option[] }) {
const { width } = useWindowDimensions();
const pieChartColors = [
"#1f77b4", // Blue
"#ff7f0e", // Orange
"#2ca02c", // Green
"#d62728", // Red
"#9467bd", // Purple
"#8c564b", // Brown
"#e377c2", // Pink
"#7f7f7f", // Gray
];
const data = options.map((option, index) => ({
name: option.label,
value: option.value,
color: pieChartColors[index],
legendFontColor: "white",
legendFontSize: 15,
}));
return (
<PieChart
data={data}
paddingLeft="0"
width={width}
height={220}
chartConfig={{
backgroundColor: "#e26a00",
backgroundGradientFrom: "#fb8c00",
backgroundGradientTo: "#ffa726",
color: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
labelColor: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
style: {
borderRadius: 16,
},
propsForDots: {
r: "6",
strokeWidth: "2",
stroke: "#ffa726",
},
}}
accessor={"value"}
backgroundColor={"transparent"}
/>
);
}

52
InsightsPage.tsx Normal file
View File

@ -0,0 +1,52 @@
import { useFocusEffect } from "@react-navigation/native";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Text } from "react-native-paper";
import AppBarChart from "./AppBarChart";
import { PADDING } from "./constants";
import { AppDataSource } from "./data-source";
import DrawerHeader from "./DrawerHeader";
import { DAYS } from "./time";
export interface WeekCounts {
week: number;
count: number;
}
export default function InsightsPage() {
const [weekCounts, setWeekCounts] = useState<WeekCounts[]>([]);
useFocusEffect(
useCallback(() => {
const select = `
SELECT strftime('%w', created) as week, COUNT(*) as count
FROM sets
WHERE created IS NOT NULL
GROUP BY week
HAVING week IS NOT NULL
ORDER BY count DESC;
`;
AppDataSource.manager.query(select).then(setWeekCounts);
}, [])
);
return (
<>
<DrawerHeader name="Insights" />
<View
style={{
padding: PADDING,
flexGrow: 1,
}}
>
<Text variant="titleLarge">Most active days of the week</Text>
<AppBarChart
options={weekCounts.map((weekCount) => ({
label: DAYS[weekCount.week],
value: weekCount.count,
}))}
/>
</View>
</>
);
}

View File

@ -9,6 +9,7 @@ import TimerPage from "./TimerPage";
import useDark from "./use-dark";
import WorkoutsPage from "./WorkoutsPage";
import WeightPage from "./WeightPage";
import InsightsPage from "./InsightsPage";
const Drawer = createDrawerNavigator<DrawerParamList>();
@ -55,6 +56,13 @@ export default function Routes() {
component={WeightPage}
options={{ drawerIcon: () => <IconButton icon="scale" /> }}
/>
<Drawer.Screen
name="Insights"
component={InsightsPage}
options={{
drawerIcon: () => <IconButton icon="lightbulb-on-outline" />,
}}
/>
<Drawer.Screen
name="Settings"
component={SettingsPage}

View File

@ -6,4 +6,5 @@ export type DrawerParamList = {
Workouts: {};
Timer: {};
Weight: {};
Insights: {};
};

175
package-lock.json generated
View File

@ -30,6 +30,7 @@
"react": "^18.2.0",
"react-hook-form": "^7.45.1",
"react-native": "^0.72.3",
"react-native-chart-kit": "^6.12.0",
"react-native-document-picker": "^9.0.1",
"react-native-file-access": "^3.0.4",
"react-native-gesture-handler": "^2.12.0",
@ -41,7 +42,7 @@
"react-native-screens": "^3.22.1",
"react-native-share": "^9.2.3",
"react-native-sqlite-storage": "^6.0.1",
"react-native-svg": "^7.0.3",
"react-native-svg": "^13.10.10",
"react-native-svg-charts": "^5.4.0",
"react-native-vector-icons": "^9.2.0",
"react-native-view-shot": "^3.7.0",
@ -4676,6 +4677,11 @@
"safe-buffer": "~5.2.0"
}
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"license": "MIT",
@ -5176,6 +5182,44 @@
"utrie": "^1.0.2"
}
},
"node_modules/css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/css-what": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/csstype": {
"version": "3.1.2",
"license": "MIT"
@ -5401,9 +5445,60 @@
"node": ">=6.0.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/dom-walk": {
"version": "0.1.2"
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dotenv": {
"version": "16.3.1",
"license": "BSD-2-Clause",
@ -5444,6 +5539,17 @@
"node": ">= 0.8"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/envinfo": {
"version": "7.10.0",
"license": "MIT",
@ -8138,6 +8244,11 @@
"tmpl": "1.0.5"
}
},
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
},
"node_modules/memoize-one": {
"version": "5.2.1",
"license": "MIT"
@ -8951,6 +9062,17 @@
"node": ">=8"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/nullthrows": {
"version": "1.1.1",
"license": "MIT"
@ -9329,6 +9451,14 @@
"node": ">=8"
}
},
"node_modules/paths-js": {
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz",
"integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==",
"engines": {
"node": ">=0.11.0"
}
},
"node_modules/pegjs": {
"version": "0.10.0",
"license": "MIT",
@ -9378,6 +9508,11 @@
"node": ">=8"
}
},
"node_modules/point-in-polygon": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
"integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"license": "MIT",
@ -9654,6 +9789,21 @@
"react": "18.2.0"
}
},
"node_modules/react-native-chart-kit": {
"version": "6.12.0",
"resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-6.12.0.tgz",
"integrity": "sha512-nZLGyCFzZ7zmX0KjYeeSV1HKuPhl1wOMlTAqa0JhlyW62qV/1ZPXHgT8o9s8mkFaGxdqbspOeuaa6I9jUQDgnA==",
"dependencies": {
"lodash": "^4.17.13",
"paths-js": "^0.4.10",
"point-in-polygon": "^1.0.1"
},
"peerDependencies": {
"react": "> 16.7.0",
"react-native": ">= 0.50.0",
"react-native-svg": "> 6.4.1"
}
},
"node_modules/react-native-document-picker": {
"version": "9.0.1",
"license": "MIT",
@ -9791,18 +9941,16 @@
}
},
"node_modules/react-native-svg": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-7.2.1.tgz",
"integrity": "sha512-9T0m/FSv8l7Vohcuc1odLHK/TCRCNWyhMGOvg6pblbhjPWTM+CSF+jgVxU4i92dKCvsYj0GYpEtuzYknyCAuqw==",
"version": "13.14.0",
"resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.14.0.tgz",
"integrity": "sha512-27ZnxUkHgWICimhuj6MuqBkISN53lVvgWJB7pIypjXysAyM+nqgQBPh4vXg+7MbqLBoYvR4PiBgKfwwGAqVxHg==",
"dependencies": {
"color": "^2.0.1",
"lodash": "^4.16.6",
"pegjs": "^0.10.0"
"css-select": "^5.1.0",
"css-tree": "^1.1.3"
},
"peerDependencies": {
"prop-types": "^15.5.7",
"react": "*",
"react-native": ">=0.50.0"
"react-native": "*"
}
},
"node_modules/react-native-svg-charts": {
@ -9821,15 +9969,6 @@
"react-native-svg": "^6.2.1||^7.0.3"
}
},
"node_modules/react-native-svg/node_modules/color": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color/-/color-2.0.1.tgz",
"integrity": "sha512-ubUCVVKfT7r2w2D3qtHakj8mbmKms+tThR8gI8zEYCbUBl8/voqFGt3kgBqGwXAopgXybnkuOq+qMYCRrp4cXw==",
"dependencies": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
}
},
"node_modules/react-native-vector-icons": {
"version": "9.2.0",
"license": "MIT",

View File

@ -33,6 +33,7 @@
"react": "^18.2.0",
"react-hook-form": "^7.45.1",
"react-native": "^0.72.3",
"react-native-chart-kit": "^6.12.0",
"react-native-document-picker": "^9.0.1",
"react-native-file-access": "^3.0.4",
"react-native-gesture-handler": "^2.12.0",
@ -44,7 +45,7 @@
"react-native-screens": "^3.22.1",
"react-native-share": "^9.2.3",
"react-native-sqlite-storage": "^6.0.1",
"react-native-svg": "^7.0.3",
"react-native-svg": "^13.10.10",
"react-native-svg-charts": "^5.4.0",
"react-native-vector-icons": "^9.2.0",
"react-native-view-shot": "^3.7.0",