1
1
mirror of https://github.com/theoludwig/p61-project.git synced 2024-07-17 07:00:12 +02:00

fix: habits statistics

This commit is contained in:
Théo LUDWIG 2024-05-22 13:44:54 +02:00
parent ca122e9fce
commit e15a3982fd
Signed by: theoludwig
GPG Key ID: ADFE5A563D718F3B
14 changed files with 683 additions and 320 deletions

View File

@ -1,4 +1,5 @@
{ {
"root": true,
"extends": [ "extends": [
"conventions", "conventions",
"plugin:react/recommended", "plugin:react/recommended",
@ -15,9 +16,6 @@
"version": "detect" "version": "detect"
} }
}, },
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": { "rules": {
"react/react-in-jsx-scope": "off", "react/react-in-jsx-scope": "off",
"react/prop-types": "off", "react/prop-types": "off",
@ -35,8 +33,9 @@
{ {
"files": ["*.ts", "*.tsx"], "files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"rules": { "plugins": ["@typescript-eslint"],
"@typescript-eslint/member-delimiter-style": "off" "parserOptions": {
"project": "./tsconfig.json"
} }
} }
] ]

View File

@ -45,7 +45,7 @@ const TabLayout: React.FC = () => {
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="habits/stats" name="habits/statistics"
options={{ options={{
title: "Statistics", title: "Statistics",
tabBarIcon: ({ color }) => { tabBarIcon: ({ color }) => {

View File

@ -0,0 +1,23 @@
import { SafeAreaView } from "react-native-safe-area-context"
import { HabitsStatistics } from "@/presentation/react-native/components/HabitsStatistics"
import { useHabitsTracker } from "@/presentation/react/contexts/HabitsTracker"
const StatisticsPage: React.FC = () => {
const { habitsTracker } = useHabitsTracker()
return (
<SafeAreaView
style={[
{
flex: 1,
backgroundColor: "white",
},
]}
>
<HabitsStatistics habitsTracker={habitsTracker} />
</SafeAreaView>
)
}
export default StatisticsPage

View File

@ -1,10 +0,0 @@
import { Stats } from "@/presentation/react/components/Stats/Stats"
import { useHabitsTracker } from "@/presentation/react/contexts/HabitsTracker"
const StatsPage: React.FC = () => {
const { habitsTracker } = useHabitsTracker()
return <Stats habitsTracker={habitsTracker} />
}
export default StatsPage

View File

@ -11,6 +11,12 @@ export const GOAL_FREQUENCIES = GOAL_FREQUENCIES_ZOD.map((frequency) => {
}) })
export type GoalFrequency = (typeof GOAL_FREQUENCIES)[number] export type GoalFrequency = (typeof GOAL_FREQUENCIES)[number]
export const GOAL_FREQUENCIES_TYPES = {
daily: "day",
weekly: "week",
monthly: "month",
} as const
export const GOAL_TYPES_ZOD = [ export const GOAL_TYPES_ZOD = [
z.literal("boolean"), z.literal("boolean"),
z.literal("numeric"), z.literal("numeric"),

View File

@ -96,4 +96,29 @@ export class HabitsTracker implements HabitsTrackerData {
) )
}) })
} }
public getHabitsStatisticsByDateAndFrequency({
selectedDate,
frequency,
}: {
selectedDate: Date
frequency: GoalFrequency
}): {
totalGoalsSuccess: number
totalGoals: number
} {
const habitsHistory = this.getHabitsHistoriesByDate({
selectedDate,
frequency,
})
let totalGoalsSuccess = 0
const totalGoals = habitsHistory.length
for (const habitHistory of habitsHistory) {
const goalProgress = habitHistory.getGoalProgressByDate(selectedDate)
if (goalProgress.isCompleted()) {
totalGoalsSuccess++
}
}
return { totalGoalsSuccess, totalGoals }
}
} }

539
package-lock.json generated
View File

@ -13,7 +13,7 @@
"@fortawesome/fontawesome-svg-core": "6.5.2", "@fortawesome/fontawesome-svg-core": "6.5.2",
"@fortawesome/free-solid-svg-icons": "6.5.2", "@fortawesome/free-solid-svg-icons": "6.5.2",
"@fortawesome/react-native-fontawesome": "0.3.1", "@fortawesome/react-native-fontawesome": "0.3.1",
"@hookform/resolvers": "3.4.0", "@hookform/resolvers": "3.4.2",
"@react-native-async-storage/async-storage": "1.23.1", "@react-native-async-storage/async-storage": "1.23.1",
"@react-navigation/native": "6.1.17", "@react-navigation/native": "6.1.17",
"@supabase/supabase-js": "2.43.2", "@supabase/supabase-js": "2.43.2",
@ -28,7 +28,7 @@
"lottie-react-native": "6.7.0", "lottie-react-native": "6.7.0",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "7.51.4", "react-hook-form": "7.51.5",
"react-native": "0.74.1", "react-native": "0.74.1",
"react-native-calendars": "1.1305.0", "react-native-calendars": "1.1305.0",
"react-native-circular-progress-indicator": "4.4.2", "react-native-circular-progress-indicator": "4.4.2",
@ -57,21 +57,21 @@
"@types/node": "20.12.12", "@types/node": "20.12.12",
"@types/react": "18.2.79", "@types/react": "18.2.79",
"@types/react-test-renderer": "18.3.0", "@types/react-test-renderer": "18.3.0",
"@typescript-eslint/eslint-plugin": "7.9.0", "@typescript-eslint/eslint-plugin": "7.10.0",
"@typescript-eslint/parser": "7.9.0", "@typescript-eslint/parser": "7.10.0",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-config-conventions": "14.1.0", "eslint-config-conventions": "14.2.0",
"eslint-plugin-import": "2.29.1", "eslint-plugin-import": "2.29.1",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-react": "7.34.1", "eslint-plugin-react": "7.34.1",
"eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-native": "4.1.0", "eslint-plugin-react-native": "4.1.0",
"eslint-plugin-unicorn": "51.0.1", "eslint-plugin-unicorn": "53.0.0",
"husky": "9.0.11", "husky": "9.0.11",
"jest": "29.7.0", "jest": "29.7.0",
"jest-expo": "51.0.2", "jest-expo": "51.0.2",
"jest-junit": "16.0.0", "jest-junit": "16.0.0",
"lint-staged": "15.2.2", "lint-staged": "15.2.4",
"prettier": "3.2.5", "prettier": "3.2.5",
"react-test-renderer": "18.2.0", "react-test-renderer": "18.2.0",
"supabase": "1.167.4", "supabase": "1.167.4",
@ -4362,9 +4362,9 @@
} }
}, },
"node_modules/@hookform/resolvers": { "node_modules/@hookform/resolvers": {
"version": "3.4.0", "version": "3.4.2",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.4.0.tgz", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.4.2.tgz",
"integrity": "sha512-+oAqK3okmoEDnvUkJ3N/mvNMeeMv5Apgy1jkoRmlaaAF4vBgcJs9tHvtXU7VE4DvPosvAUUkPOaNFunzt1dbgA==", "integrity": "sha512-1m9uAVIO8wVf7VCDAGsuGA0t6Z3m6jVGAN50HkV9vYLl0yixKK/Z1lr01vaRvYCkIKGoy1noVRxMzQYb4y/j1Q==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"react-hook-form": "^7.0.0" "react-hook-form": "^7.0.0"
@ -8751,9 +8751,9 @@
} }
}, },
"node_modules/@types/babel__traverse": { "node_modules/@types/babel__traverse": {
"version": "7.20.5", "version": "7.20.6",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz",
"integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -8966,17 +8966,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz",
"integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.9.0", "@typescript-eslint/scope-manager": "7.10.0",
"@typescript-eslint/type-utils": "7.9.0", "@typescript-eslint/type-utils": "7.10.0",
"@typescript-eslint/utils": "7.9.0", "@typescript-eslint/utils": "7.10.0",
"@typescript-eslint/visitor-keys": "7.9.0", "@typescript-eslint/visitor-keys": "7.10.0",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.3.1", "ignore": "^5.3.1",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@ -9000,16 +9000,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz",
"integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", "integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "7.9.0", "@typescript-eslint/scope-manager": "7.10.0",
"@typescript-eslint/types": "7.9.0", "@typescript-eslint/types": "7.10.0",
"@typescript-eslint/typescript-estree": "7.9.0", "@typescript-eslint/typescript-estree": "7.10.0",
"@typescript-eslint/visitor-keys": "7.9.0", "@typescript-eslint/visitor-keys": "7.10.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -9029,14 +9029,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz",
"integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "7.9.0", "@typescript-eslint/types": "7.10.0",
"@typescript-eslint/visitor-keys": "7.9.0" "@typescript-eslint/visitor-keys": "7.10.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || >=20.0.0" "node": "^18.18.0 || >=20.0.0"
@ -9047,14 +9047,14 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz",
"integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "7.9.0", "@typescript-eslint/typescript-estree": "7.10.0",
"@typescript-eslint/utils": "7.9.0", "@typescript-eslint/utils": "7.10.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^1.3.0" "ts-api-utils": "^1.3.0"
}, },
@ -9075,9 +9075,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz",
"integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -9089,14 +9089,14 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz",
"integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "7.9.0", "@typescript-eslint/types": "7.10.0",
"@typescript-eslint/visitor-keys": "7.9.0", "@typescript-eslint/visitor-keys": "7.10.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"globby": "^11.1.0", "globby": "^11.1.0",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@ -9131,16 +9131,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz",
"integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@typescript-eslint/scope-manager": "7.9.0", "@typescript-eslint/scope-manager": "7.10.0",
"@typescript-eslint/types": "7.9.0", "@typescript-eslint/types": "7.10.0",
"@typescript-eslint/typescript-estree": "7.9.0" "@typescript-eslint/typescript-estree": "7.10.0"
}, },
"engines": { "engines": {
"node": "^18.18.0 || >=20.0.0" "node": "^18.18.0 || >=20.0.0"
@ -9154,13 +9154,13 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "7.9.0", "version": "7.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz",
"integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "7.9.0", "@typescript-eslint/types": "7.10.0",
"eslint-visitor-keys": "^3.4.3" "eslint-visitor-keys": "^3.4.3"
}, },
"engines": { "engines": {
@ -9978,9 +9978,9 @@
} }
}, },
"node_modules/babel-plugin-react-native-web": { "node_modules/babel-plugin-react-native-web": {
"version": "0.19.11", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.11.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz",
"integrity": "sha512-0sHf8GgDhsRZxGwlwHHdfL3U8wImFaLw4haEa60U9M3EiO3bg6u3BJ+1vXhwgrevqSq76rMb5j1HJs+dNvMj5g==", "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/babel-plugin-transform-flow-enums": { "node_modules/babel-plugin-transform-flow-enums": {
@ -10189,12 +10189,12 @@
} }
}, },
"node_modules/braces": { "node_modules/braces": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fill-range": "^7.0.1" "fill-range": "^7.1.1"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -10345,13 +10345,13 @@
} }
}, },
"node_modules/cacache/node_modules/glob": { "node_modules/cacache/node_modules/glob": {
"version": "10.3.15", "version": "10.3.16",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz",
"integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"foreground-child": "^3.1.0", "foreground-child": "^3.1.0",
"jackspeak": "^2.3.6", "jackspeak": "^3.1.2",
"minimatch": "^9.0.1", "minimatch": "^9.0.1",
"minipass": "^7.0.4", "minipass": "^7.0.4",
"path-scurry": "^1.11.0" "path-scurry": "^1.11.0"
@ -10446,9 +10446,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001620", "version": "1.0.30001621",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz",
"integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -11992,9 +11992,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.774", "version": "1.4.777",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.777.tgz",
"integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", "integrity": "sha512-n02NCwLJ3wexLfK/yQeqfywCblZqLcXphzmid5e8yVPdtEcida7li0A5WQKghHNG0FeOMCzeFOzEbtAh5riXFw==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/emittery": { "node_modules/emittery": {
@ -12392,20 +12392,19 @@
} }
}, },
"node_modules/eslint-config-conventions": { "node_modules/eslint-config-conventions": {
"version": "14.1.0", "version": "14.2.0",
"resolved": "https://registry.npmjs.org/eslint-config-conventions/-/eslint-config-conventions-14.1.0.tgz", "resolved": "https://registry.npmjs.org/eslint-config-conventions/-/eslint-config-conventions-14.2.0.tgz",
"integrity": "sha512-oJOQcUfXD874XuJOCtoRmjuVKAJ+6TG92So0VDgwvsBx3R2pVAc34CmuvAhqamFeaNOkLTSFu/mVrnmZggzgIw==", "integrity": "sha512-f6BY4d5ibQfNY4MYRN5jat/bCZW/m/6lRXAca5cc3Q84DnTYwimby1GutBvnKfiA+/ZhIUcgqbFZozLOldBIhQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18.0.0", "node": ">=18.0.0"
"npm": ">=10.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-promise": "^6.1.1", "eslint-plugin-promise": "^6.1.1",
"eslint-plugin-unicorn": "^51.0.1" "eslint-plugin-unicorn": "^51.0.1 || ^52.0.0 || ^53.0.0"
} }
}, },
"node_modules/eslint-import-resolver-node": { "node_modules/eslint-import-resolver-node": {
@ -12672,18 +12671,18 @@
} }
}, },
"node_modules/eslint-plugin-unicorn": { "node_modules/eslint-plugin-unicorn": {
"version": "51.0.1", "version": "53.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-53.0.0.tgz",
"integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", "integrity": "sha512-kuTcNo9IwwUCfyHGwQFOK/HjJAYzbODHN3wP0PgqbW+jbXqpNWxNVpVhj2tO9SixBwuAdmal8rVcWKBxwFnGuw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.22.20", "@babel/helper-validator-identifier": "^7.24.5",
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"@eslint/eslintrc": "^2.1.4", "@eslint/eslintrc": "^3.0.2",
"ci-info": "^4.0.0", "ci-info": "^4.0.0",
"clean-regexp": "^1.0.0", "clean-regexp": "^1.0.0",
"core-js-compat": "^3.34.0", "core-js-compat": "^3.37.0",
"esquery": "^1.5.0", "esquery": "^1.5.0",
"indent-string": "^4.0.0", "indent-string": "^4.0.0",
"is-builtin-module": "^3.2.1", "is-builtin-module": "^3.2.1",
@ -12692,11 +12691,11 @@
"read-pkg-up": "^7.0.1", "read-pkg-up": "^7.0.1",
"regexp-tree": "^0.1.27", "regexp-tree": "^0.1.27",
"regjsparser": "^0.10.0", "regjsparser": "^0.10.0",
"semver": "^7.5.4", "semver": "^7.6.1",
"strip-indent": "^3.0.0" "strip-indent": "^3.0.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=18.18"
}, },
"funding": { "funding": {
"url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
@ -12705,6 +12704,102 @@
"eslint": ">=8.56.0" "eslint": ">=8.56.0"
} }
}, },
"node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
"integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
"espree": "^10.0.1",
"globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
"minimatch": "^3.1.2",
"strip-json-comments": "^3.1.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
"integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/espree": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
"integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.11.3",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/globals": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/jsesc": { "node_modules/eslint-plugin-unicorn/node_modules/jsesc": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
@ -12718,6 +12813,26 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/eslint-plugin-unicorn/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true,
"license": "MIT"
},
"node_modules/eslint-plugin-unicorn/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/eslint-plugin-unicorn/node_modules/semver": { "node_modules/eslint-plugin-unicorn/node_modules/semver": {
"version": "7.6.2", "version": "7.6.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
@ -13725,9 +13840,9 @@
} }
}, },
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
@ -14075,6 +14190,20 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -15638,9 +15767,9 @@
} }
}, },
"node_modules/jackspeak": { "node_modules/jackspeak": {
"version": "2.3.6", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
"license": "BlueOak-1.0.0", "license": "BlueOak-1.0.0",
"dependencies": { "dependencies": {
"@isaacs/cliui": "^8.0.2" "@isaacs/cliui": "^8.0.2"
@ -18378,6 +18507,106 @@
"lightningcss-win32-x64-msvc": "1.19.0" "lightningcss-win32-x64-msvc": "1.19.0"
} }
}, },
"node_modules/lightningcss-darwin-arm64": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz",
"integrity": "sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz",
"integrity": "sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.19.0.tgz",
"integrity": "sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig==",
"cpu": [
"arm"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.19.0.tgz",
"integrity": "sha512-zwXRjWqpev8wqO0sv0M1aM1PpjHz6RVIsBcxKszIG83Befuh4yNysjgHVplF9RTU7eozGe3Ts7r6we1+Qkqsww==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.19.0.tgz",
"integrity": "sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": { "node_modules/lightningcss-linux-x64-gnu": {
"version": "1.19.0", "version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.19.0.tgz", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.19.0.tgz",
@ -18418,14 +18647,37 @@
"url": "https://opencollective.com/parcel" "url": "https://opencollective.com/parcel"
} }
}, },
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.19.0.tgz",
"integrity": "sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lilconfig": { "node_modules/lilconfig": {
"version": "3.0.0", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
"integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=14" "node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
} }
}, },
"node_modules/lines-and-columns": { "node_modules/lines-and-columns": {
@ -18435,22 +18687,22 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/lint-staged": { "node_modules/lint-staged": {
"version": "15.2.2", "version": "15.2.4",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.4.tgz",
"integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", "integrity": "sha512-3F9KRQIS2fVDGtCkBp4Bx0jswjX7zUcKx6OF0ZeY1prksUyKPRIIUqZhIUYAstJfvj6i48VFs4dwVIbCYwvTYQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"chalk": "5.3.0", "chalk": "5.3.0",
"commander": "11.1.0", "commander": "12.1.0",
"debug": "4.3.4", "debug": "4.3.4",
"execa": "8.0.1", "execa": "8.0.1",
"lilconfig": "3.0.0", "lilconfig": "3.1.1",
"listr2": "8.0.1", "listr2": "8.2.1",
"micromatch": "4.0.5", "micromatch": "4.0.6",
"pidtree": "0.6.0", "pidtree": "0.6.0",
"string-argv": "0.3.2", "string-argv": "0.3.2",
"yaml": "2.3.4" "yaml": "2.4.2"
}, },
"bin": { "bin": {
"lint-staged": "bin/lint-staged.js" "lint-staged": "bin/lint-staged.js"
@ -18476,19 +18728,46 @@
} }
}, },
"node_modules/lint-staged/node_modules/commander": { "node_modules/lint-staged/node_modules/commander": {
"version": "11.1.0", "version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=16" "node": ">=18"
}
},
"node_modules/lint-staged/node_modules/micromatch": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.6.tgz",
"integrity": "sha512-Y4Ypn3oujJYxJcMacVgcs92wofTHxp9FzfDpQON4msDefoC0lb3ETvQLOdLcbhSwU1bz8HrL/1sygfBIHudrkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/lint-staged/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/listr2": { "node_modules/listr2": {
"version": "8.0.1", "version": "8.2.1",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz",
"integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -18496,7 +18775,7 @@
"colorette": "^2.0.20", "colorette": "^2.0.20",
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"log-update": "^6.0.0", "log-update": "^6.0.0",
"rfdc": "^1.3.0", "rfdc": "^1.3.1",
"wrap-ansi": "^9.0.0" "wrap-ansi": "^9.0.0"
}, },
"engines": { "engines": {
@ -19741,12 +20020,12 @@
} }
}, },
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.5", "version": "4.0.7",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"braces": "^3.0.2", "braces": "^3.0.3",
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
}, },
"engines": { "engines": {
@ -20743,6 +21022,7 @@
"version": "0.1.5", "version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"deprecated": "This package is no longer supported.",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"os-homedir": "^1.0.0", "os-homedir": "^1.0.0",
@ -21565,9 +21845,9 @@
} }
}, },
"node_modules/react-hook-form": { "node_modules/react-hook-form": {
"version": "7.51.4", "version": "7.51.5",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.4.tgz", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.5.tgz",
"integrity": "sha512-V14i8SEkh+V1gs6YtD0hdHYnoL4tp/HX/A45wWQN15CYr9bFRmmRdYStSO5L65lCCZRF+kYiSKhm9alqbcdiVA==", "integrity": "sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=12.22.0" "node": ">=12.22.0"
@ -23910,14 +24190,14 @@
} }
}, },
"node_modules/supabase/node_modules/glob": { "node_modules/supabase/node_modules/glob": {
"version": "10.3.15", "version": "10.3.16",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz",
"integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"foreground-child": "^3.1.0", "foreground-child": "^3.1.0",
"jackspeak": "^2.3.6", "jackspeak": "^3.1.2",
"minimatch": "^9.0.1", "minimatch": "^9.0.1",
"minipass": "^7.0.4", "minipass": "^7.0.4",
"path-scurry": "^1.11.0" "path-scurry": "^1.11.0"
@ -24798,9 +25078,9 @@
} }
}, },
"node_modules/undici": { "node_modules/undici": {
"version": "6.17.0", "version": "6.18.1",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.17.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.1.tgz",
"integrity": "sha512-fs13QiDjPIzJ7gFAOal9CSG0c92rT2xw6MuMUJ4H30Eg5GCauLWYCCZA1tInjd6M4y+JZjVCCFr9pFpbhcC64w==", "integrity": "sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18.17" "node": ">=18.17"
@ -25640,10 +25920,13 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.3.4", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz",
"integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==",
"license": "ISC", "license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
} }

View File

@ -24,7 +24,7 @@
"@fortawesome/fontawesome-svg-core": "6.5.2", "@fortawesome/fontawesome-svg-core": "6.5.2",
"@fortawesome/free-solid-svg-icons": "6.5.2", "@fortawesome/free-solid-svg-icons": "6.5.2",
"@fortawesome/react-native-fontawesome": "0.3.1", "@fortawesome/react-native-fontawesome": "0.3.1",
"@hookform/resolvers": "3.4.0", "@hookform/resolvers": "3.4.2",
"@react-native-async-storage/async-storage": "1.23.1", "@react-native-async-storage/async-storage": "1.23.1",
"@react-navigation/native": "6.1.17", "@react-navigation/native": "6.1.17",
"@supabase/supabase-js": "2.43.2", "@supabase/supabase-js": "2.43.2",
@ -39,7 +39,7 @@
"lottie-react-native": "6.7.0", "lottie-react-native": "6.7.0",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "7.51.4", "react-hook-form": "7.51.5",
"react-native": "0.74.1", "react-native": "0.74.1",
"react-native-calendars": "1.1305.0", "react-native-calendars": "1.1305.0",
"react-native-circular-progress-indicator": "4.4.2", "react-native-circular-progress-indicator": "4.4.2",
@ -68,21 +68,21 @@
"@types/node": "20.12.12", "@types/node": "20.12.12",
"@types/react": "18.2.79", "@types/react": "18.2.79",
"@types/react-test-renderer": "18.3.0", "@types/react-test-renderer": "18.3.0",
"@typescript-eslint/eslint-plugin": "7.9.0", "@typescript-eslint/eslint-plugin": "7.10.0",
"@typescript-eslint/parser": "7.9.0", "@typescript-eslint/parser": "7.10.0",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-config-conventions": "14.1.0", "eslint-config-conventions": "14.2.0",
"eslint-plugin-import": "2.29.1", "eslint-plugin-import": "2.29.1",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-react": "7.34.1", "eslint-plugin-react": "7.34.1",
"eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-native": "4.1.0", "eslint-plugin-react-native": "4.1.0",
"eslint-plugin-unicorn": "51.0.1", "eslint-plugin-unicorn": "53.0.0",
"husky": "9.0.11", "husky": "9.0.11",
"jest": "29.7.0", "jest": "29.7.0",
"jest-expo": "51.0.2", "jest-expo": "51.0.2",
"jest-junit": "16.0.0", "jest-junit": "16.0.0",
"lint-staged": "15.2.2", "lint-staged": "15.2.4",
"prettier": "3.2.5", "prettier": "3.2.5",
"react-test-renderer": "18.2.0", "react-test-renderer": "18.2.0",
"supabase": "1.167.4", "supabase": "1.167.4",

View File

@ -6,7 +6,7 @@ import { Divider, List, Text } from "react-native-paper"
import { GOAL_FREQUENCIES, type GoalFrequency } from "@/domain/entities/Goal" import { GOAL_FREQUENCIES, type GoalFrequency } from "@/domain/entities/Goal"
import type { HabitHistory } from "@/domain/entities/HabitHistory" import type { HabitHistory } from "@/domain/entities/HabitHistory"
import type { HabitsTracker } from "@/domain/entities/HabitsTracker" import type { HabitsTracker } from "@/domain/entities/HabitsTracker"
import { capitalize } from "@/utils/strings" import { LOCALE, capitalize } from "@/utils/strings"
import confettiJSON from "../../../assets/confetti.json" import confettiJSON from "../../../assets/confetti.json"
import { HabitCard } from "./HabitCard" import { HabitCard } from "./HabitCard"
@ -96,7 +96,7 @@ export const HabitsList: React.FC<HabitsListProps> = (props) => {
marginTop: 20, marginTop: 20,
}} }}
> >
{selectedDate.toLocaleDateString("en-US", { {selectedDate.toLocaleDateString(LOCALE, {
weekday: "long", weekday: "long",
year: "numeric", year: "numeric",
month: "long", month: "long",

View File

@ -0,0 +1,110 @@
import { Card, Divider, Text } from "react-native-paper"
import CircularProgress from "react-native-circular-progress-indicator"
import { Agenda } from "react-native-calendars"
import { useState } from "react"
import { getNowDateUTC, getISODate } from "@/utils/dates"
import type { HabitsTracker } from "@/domain/entities/HabitsTracker"
import { LOCALE } from "@/utils/strings"
import {
GOAL_FREQUENCIES,
GOAL_FREQUENCIES_TYPES,
} from "@/domain/entities/Goal"
import { calculateRatio } from "@/utils/maths"
export interface HabitsStatisticsProps {
habitsTracker: HabitsTracker
}
export const HabitsStatistics: React.FC<HabitsStatisticsProps> = (props) => {
const { habitsTracker } = props
const today = getNowDateUTC()
const todayISO = getISODate(today)
const [selectedDate, setSelectedDate] = useState<Date>(today)
const selectedDateISO = getISODate(selectedDate)
return (
<Agenda
firstDay={1}
showClosingKnob
onDayPress={(date) => {
setSelectedDate(new Date(date.dateString))
}}
markedDates={{
[todayISO]: { marked: true, today: true },
}}
maxDate={todayISO}
selected={selectedDateISO}
renderList={() => {
return (
<>
<Divider />
<Text
style={{
fontWeight: "bold",
fontSize: 22,
textAlign: "center",
marginVertical: 10,
}}
>
{selectedDate.toLocaleDateString(LOCALE, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
})}
</Text>
{GOAL_FREQUENCIES.map((frequency) => {
const { totalGoalsSuccess, totalGoals } =
habitsTracker.getHabitsStatisticsByDateAndFrequency({
selectedDate,
frequency,
})
const percentage =
calculateRatio(totalGoalsSuccess, totalGoals) * 100
return {
totalGoalsSuccess,
totalGoals,
percentage,
frequency,
}
})
.filter(({ totalGoals }) => {
return totalGoals > 0
})
.map(
({ frequency, totalGoals, totalGoalsSuccess, percentage }) => {
return (
<Card
key={frequency}
mode="elevated"
style={{ marginVertical: 8, marginHorizontal: 10 }}
>
<Card.Content>
<Text variant="bodyMedium">
{totalGoalsSuccess} achieved goals in the{" "}
{GOAL_FREQUENCIES_TYPES[frequency]} out of{" "}
{totalGoals}.
</Text>
<CircularProgress
value={percentage}
progressValueColor={"#ecf0f1"}
circleBackgroundColor="black"
titleColor="white"
title="%"
/>
</Card.Content>
</Card>
)
},
)}
</>
)
}}
/>
)
}

View File

@ -1,167 +0,0 @@
import { Card, Text } from "react-native-paper"
import CircularProgress from "react-native-circular-progress-indicator"
import { Agenda } from "react-native-calendars"
import { useState } from "react"
import { getNowDateUTC, getISODate } from "@/utils/dates"
import type { HabitsTracker } from "@/domain/entities/HabitsTracker"
export interface StatsProps {
habitsTracker: HabitsTracker
}
export const Stats: React.FC<StatsProps> = (props) => {
const { habitsTracker } = props
const today = getNowDateUTC()
const todayISO = getISODate(today)
const [selectedDate, setSelectedDate] = useState<Date>(today)
const selectedDateISO = getISODate(selectedDate)
const habitsHistory = habitsTracker.getAllHabitsHistory()
let goalDays = 0
let totalGoalDays = 0
const dailyHabits = habitsHistory.filter((el) => {
return (
el.habit.goal.frequency === "daily" && el.habit.startDate <= selectedDate
)
})
let displayDaily = true
if (dailyHabits.length === 0) {
displayDaily = false
} else {
for (const el of dailyHabits) {
totalGoalDays++
if (
el.getProgressesByDate(selectedDate)[0]?.goalProgress.isCompleted() ??
false
) {
goalDays++
}
}
}
let goalWeek = 0
let totalGoalWeek = 0
const weeklyHabits = habitsHistory.filter((el) => {
return (
el.habit.goal.frequency === "weekly" && el.habit.startDate <= selectedDate
)
})
let displayWeekly = true
if (weeklyHabits.length === 0) {
displayWeekly = false
} else {
for (const el of weeklyHabits) {
totalGoalWeek++
if (
el.getProgressesByDate(selectedDate)[0]?.goalProgress.isCompleted() ??
false
) {
goalWeek++
}
}
}
let goalMonth = 0
let totalGoalMonth = 0
const monthlyHabits = habitsHistory.filter((el) => {
return (
el.habit.goal.frequency === "monthly" &&
el.habit.startDate <= selectedDate
)
})
let displayMonthly = true
if (monthlyHabits.length === 0) {
displayMonthly = false
} else {
for (const el of monthlyHabits) {
totalGoalMonth++
if (
el.getProgressesByDate(selectedDate)[0]?.goalProgress.isCompleted() ??
false
) {
goalMonth++
}
}
}
return (
<Agenda
firstDay={1}
showClosingKnob
onDayPress={(date) => {
setSelectedDate(new Date(date.dateString))
}}
markedDates={{
[todayISO]: { marked: true, today: true },
}}
maxDate={todayISO}
selected={selectedDateISO}
renderList={() => {
return (
<>
{displayDaily ? (
<Card key="statsDay" mode="outlined">
<Card.Title title="Sucess Day" />
<Card.Content>
<Text variant="bodyMedium">
{goalDays} but réussi dans la journée sur {totalGoalDays}
</Text>
<CircularProgress
value={(goalDays / totalGoalDays) * 100}
activeStrokeWidth={12}
progressValueColor={"#ecf0f1"}
circleBackgroundColor="black"
titleColor="white"
title="%"
/>
</Card.Content>
</Card>
) : null}
{displayWeekly ? (
<Card key="statsWeek" mode="outlined">
<Card.Title title="Sucess Week" />
<Card.Content>
<Text variant="bodyMedium">
{goalWeek} but réussi dans la semaine sur {totalGoalWeek}
</Text>
<CircularProgress
value={(goalWeek / totalGoalWeek) * 100}
activeStrokeWidth={12}
progressValueColor={"#ecf0f1"}
circleBackgroundColor="black"
titleColor="white"
title="%"
/>
</Card.Content>
</Card>
) : null}
{displayMonthly ? (
<Card key="statsMonth" mode="outlined">
<Card.Title title="Sucess Month" />
<Card.Content>
<Text variant="bodyMedium">
{goalMonth} but réussi dans le mois sur {totalGoalMonth}
</Text>
<CircularProgress
value={(goalMonth / totalGoalMonth) * 100}
activeStrokeWidth={12}
progressValueColor={"#ecf0f1"}
circleBackgroundColor="black"
titleColor="white"
title="%"
/>
</Card.Content>
</Card>
) : null}
</>
)
}}
/>
)
}

View File

@ -0,0 +1,83 @@
import { calculateRatio } from "../maths"
describe("utils/maths", () => {
describe("calculateRatio", () => {
it("should calculate the ratio of a value to a total", () => {
// Arrange - Given
const value = 3
const total = 10
// Act - When
const result = calculateRatio(value, total)
// Assert - Then
const expected = 0.3
expect(result).toEqual(expected)
})
it("should return 0 if the total is 0", () => {
// Arrange - Given
const value = 3
const total = 0
// Act - When
const result = calculateRatio(value, total)
// Assert - Then
const expected = 0
expect(result).toEqual(expected)
})
it("should return 0 if the total is negative", () => {
// Arrange - Given
const value = 3
const total = -1
// Act - When
const result = calculateRatio(value, total)
// Assert - Then
const expected = 0
expect(result).toEqual(expected)
})
it("should return 0 if the value is 0", () => {
// Arrange - Given
const value = 0
const total = 10
// Act - When
const result = calculateRatio(value, total)
// Assert - Then
const expected = 0
expect(result).toEqual(expected)
})
it("should return 1 if the value is equal to the total", () => {
// Arrange - Given
const value = 10
const total = 10
// Act - When
const result = calculateRatio(value, total)
// Assert - Then
const expected = 1
expect(result).toEqual(expected)
})
it("should return 1 if the value is greater than the total", () => {
// Arrange - Given
const value = 11
const total = 10
// Act - When
const result = calculateRatio(value, total)
// Assert - Then
const expected = 1
expect(result).toEqual(expected)
})
})
})

9
utils/maths.ts Normal file
View File

@ -0,0 +1,9 @@
export const calculateRatio = (value: number, total: number): number => {
if (total <= 0) {
return 0
}
if (value >= total) {
return 1
}
return value / total
}

View File

@ -1,3 +1,5 @@
export const LOCALE = "en-US"
export const capitalize = (string: string): string => { export const capitalize = (string: string): string => {
return string.charAt(0).toUpperCase() + string.slice(1) return string.charAt(0).toUpperCase() + string.slice(1)
} }