diff --git a/.eslintrc.json b/.eslintrc.json index 743a7e9..9de8abe 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,5 @@ { + "root": true, "extends": [ "conventions", "plugin:react/recommended", @@ -15,9 +16,6 @@ "version": "detect" } }, - "parserOptions": { - "project": "./tsconfig.json" - }, "rules": { "react/react-in-jsx-scope": "off", "react/prop-types": "off", @@ -35,8 +33,9 @@ { "files": ["*.ts", "*.tsx"], "parser": "@typescript-eslint/parser", - "rules": { - "@typescript-eslint/member-delimiter-style": "off" + "plugins": ["@typescript-eslint"], + "parserOptions": { + "project": "./tsconfig.json" } } ] diff --git a/app/application/_layout.tsx b/app/application/_layout.tsx index 67cad6f..03215f6 100644 --- a/app/application/_layout.tsx +++ b/app/application/_layout.tsx @@ -45,11 +45,11 @@ const TabLayout: React.FC = () => { }} /> { - return + return }, }} /> diff --git a/app/application/habits/history.tsx b/app/application/habits/history.tsx deleted file mode 100644 index 4975421..0000000 --- a/app/application/habits/history.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useMemo, useState } from "react" -import { View } from "react-native" -import { Agenda } from "react-native-calendars" -import { Text } from "react-native-paper" -import { SafeAreaView } from "react-native-safe-area-context" - -import { getISODate, getNowDateUTC } from "@/utils/dates" - -const HistoryPage: React.FC = () => { - const today = useMemo(() => { - return getNowDateUTC() - }, []) - const todayISO = getISODate(today) - - const [selectedDate, setSelectedDate] = useState(today) - const selectedISODate = getISODate(selectedDate) - - return ( - - { - setSelectedDate(new Date(date.dateString)) - }} - markedDates={{ - [todayISO]: { marked: true }, - }} - selected={selectedISODate} - renderList={() => { - return ( - - {selectedDate.toISOString()} - - ) - }} - /> - - ) -} - -export default HistoryPage diff --git a/app/application/habits/statistics.tsx b/app/application/habits/statistics.tsx new file mode 100644 index 0000000..c9fb2ac --- /dev/null +++ b/app/application/habits/statistics.tsx @@ -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 ( + + + + ) +} + +export default StatisticsPage diff --git a/domain/entities/Goal.ts b/domain/entities/Goal.ts index e3c4077..371314e 100644 --- a/domain/entities/Goal.ts +++ b/domain/entities/Goal.ts @@ -11,6 +11,12 @@ export const GOAL_FREQUENCIES = GOAL_FREQUENCIES_ZOD.map((frequency) => { }) 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 = [ z.literal("boolean"), z.literal("numeric"), diff --git a/domain/entities/HabitsTracker.ts b/domain/entities/HabitsTracker.ts index e7e62d9..4b601e8 100644 --- a/domain/entities/HabitsTracker.ts +++ b/domain/entities/HabitsTracker.ts @@ -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 } + } } diff --git a/package-lock.json b/package-lock.json index 7f6acc7..ddbdb4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@fortawesome/fontawesome-svg-core": "6.5.2", "@fortawesome/free-solid-svg-icons": "6.5.2", "@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-navigation/native": "6.1.17", "@supabase/supabase-js": "2.43.2", @@ -28,9 +28,10 @@ "lottie-react-native": "6.7.0", "react": "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-calendars": "1.1305.0", + "react-native-circular-progress-indicator": "4.4.2", "react-native-elements": "3.4.3", "react-native-gesture-handler": "2.16.2", "react-native-paper": "5.12.3", @@ -56,21 +57,21 @@ "@types/node": "20.12.12", "@types/react": "18.2.79", "@types/react-test-renderer": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.9.0", - "@typescript-eslint/parser": "7.9.0", + "@typescript-eslint/eslint-plugin": "7.10.0", + "@typescript-eslint/parser": "7.10.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-promise": "6.1.1", "eslint-plugin-react": "7.34.1", "eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-native": "4.1.0", - "eslint-plugin-unicorn": "51.0.1", + "eslint-plugin-unicorn": "53.0.0", "husky": "9.0.11", "jest": "29.7.0", "jest-expo": "51.0.2", "jest-junit": "16.0.0", - "lint-staged": "15.2.2", + "lint-staged": "15.2.4", "prettier": "3.2.5", "react-test-renderer": "18.2.0", "supabase": "1.167.4", @@ -4361,9 +4362,9 @@ } }, "node_modules/@hookform/resolvers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.4.0.tgz", - "integrity": "sha512-+oAqK3okmoEDnvUkJ3N/mvNMeeMv5Apgy1jkoRmlaaAF4vBgcJs9tHvtXU7VE4DvPosvAUUkPOaNFunzt1dbgA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.4.2.tgz", + "integrity": "sha512-1m9uAVIO8wVf7VCDAGsuGA0t6Z3m6jVGAN50HkV9vYLl0yixKK/Z1lr01vaRvYCkIKGoy1noVRxMzQYb4y/j1Q==", "license": "MIT", "peerDependencies": { "react-hook-form": "^7.0.0" @@ -8750,9 +8751,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "license": "MIT", "dependencies": { @@ -8965,17 +8966,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", - "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz", + "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/type-utils": "7.9.0", - "@typescript-eslint/utils": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/type-utils": "7.10.0", + "@typescript-eslint/utils": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -8999,16 +9000,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz", + "integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4" }, "engines": { @@ -9028,14 +9029,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", - "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -9046,14 +9047,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", - "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz", + "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/utils": "7.9.0", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/utils": "7.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -9074,9 +9075,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", - "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", "dev": true, "license": "MIT", "engines": { @@ -9088,14 +9089,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", - "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -9130,16 +9131,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", - "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0" + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -9153,13 +9154,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", - "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/types": "7.10.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -9245,6 +9246,12 @@ "node": ">=6.5" } }, + "node_modules/abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==", + "license": "MIT" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -9971,9 +9978,9 @@ } }, "node_modules/babel-plugin-react-native-web": { - "version": "0.19.11", - "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.11.tgz", - "integrity": "sha512-0sHf8GgDhsRZxGwlwHHdfL3U8wImFaLw4haEa60U9M3EiO3bg6u3BJ+1vXhwgrevqSq76rMb5j1HJs+dNvMj5g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.12.tgz", + "integrity": "sha512-eYZ4+P6jNcB37lObWIg0pUbi7+3PKoU1Oie2j0C8UF3cXyXoR74tO2NBjI/FORb2LJyItJZEAmjU5pSaJYEL1w==", "license": "MIT" }, "node_modules/babel-plugin-transform-flow-enums": { @@ -10182,12 +10189,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -10338,13 +10345,13 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.3.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", - "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", + "jackspeak": "^3.1.2", "minimatch": "^9.0.1", "minipass": "^7.0.4", "path-scurry": "^1.11.0" @@ -10439,9 +10446,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001620", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", - "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", + "version": "1.0.30001621", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz", + "integrity": "sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==", "funding": [ { "type": "opencollective", @@ -11985,9 +11992,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.774", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", - "integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", + "version": "1.4.777", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.777.tgz", + "integrity": "sha512-n02NCwLJ3wexLfK/yQeqfywCblZqLcXphzmid5e8yVPdtEcida7li0A5WQKghHNG0FeOMCzeFOzEbtAh5riXFw==", "license": "ISC" }, "node_modules/emittery": { @@ -12385,20 +12392,19 @@ } }, "node_modules/eslint-config-conventions": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-conventions/-/eslint-config-conventions-14.1.0.tgz", - "integrity": "sha512-oJOQcUfXD874XuJOCtoRmjuVKAJ+6TG92So0VDgwvsBx3R2pVAc34CmuvAhqamFeaNOkLTSFu/mVrnmZggzgIw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-conventions/-/eslint-config-conventions-14.2.0.tgz", + "integrity": "sha512-f6BY4d5ibQfNY4MYRN5jat/bCZW/m/6lRXAca5cc3Q84DnTYwimby1GutBvnKfiA+/ZhIUcgqbFZozLOldBIhQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18.0.0", - "npm": ">=10.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "eslint": "^8.56.0", "eslint-plugin-import": "^2.29.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": { @@ -12665,18 +12671,18 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "53.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-53.0.0.tgz", + "integrity": "sha512-kuTcNo9IwwUCfyHGwQFOK/HjJAYzbODHN3wP0PgqbW+jbXqpNWxNVpVhj2tO9SixBwuAdmal8rVcWKBxwFnGuw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.5", "@eslint-community/eslint-utils": "^4.4.0", - "@eslint/eslintrc": "^2.1.4", + "@eslint/eslintrc": "^3.0.2", "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.34.0", + "core-js-compat": "^3.37.0", "esquery": "^1.5.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", @@ -12685,11 +12691,11 @@ "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.27", "regjsparser": "^0.10.0", - "semver": "^7.5.4", + "semver": "^7.6.1", "strip-indent": "^3.0.0" }, "engines": { - "node": ">=16" + "node": ">=18.18" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" @@ -12698,6 +12704,102 @@ "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": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", @@ -12711,6 +12813,26 @@ "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": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", @@ -13718,9 +13840,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -15645,9 +15767,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -18546,13 +18668,16 @@ } }, "node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", "dev": true, "license": "MIT", "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -18562,22 +18687,22 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", - "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.4.tgz", + "integrity": "sha512-3F9KRQIS2fVDGtCkBp4Bx0jswjX7zUcKx6OF0ZeY1prksUyKPRIIUqZhIUYAstJfvj6i48VFs4dwVIbCYwvTYQ==", "dev": true, "license": "MIT", "dependencies": { "chalk": "5.3.0", - "commander": "11.1.0", + "commander": "12.1.0", "debug": "4.3.4", "execa": "8.0.1", - "lilconfig": "3.0.0", - "listr2": "8.0.1", - "micromatch": "4.0.5", + "lilconfig": "3.1.1", + "listr2": "8.2.1", + "micromatch": "4.0.6", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.4" + "yaml": "2.4.2" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -18603,19 +18728,46 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "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": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", - "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", + "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", "dev": true, "license": "MIT", "dependencies": { @@ -18623,7 +18775,7 @@ "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.0.0", - "rfdc": "^1.3.0", + "rfdc": "^1.3.1", "wrap-ansi": "^9.0.0" }, "engines": { @@ -19868,12 +20020,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -20435,6 +20587,15 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-svg-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", + "license": "MIT", + "dependencies": { + "svg-arc-to-cubic-bezier": "^3.0.0" + } + }, "node_modules/npm-normalize-package-bin": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", @@ -20861,6 +21022,7 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "deprecated": "This package is no longer supported.", "license": "ISC", "dependencies": { "os-homedir": "^1.0.0", @@ -21002,6 +21164,12 @@ "node": ">=10" } }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", + "license": "MIT" + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -21677,9 +21845,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.51.4", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.4.tgz", - "integrity": "sha512-V14i8SEkh+V1gs6YtD0hdHYnoL4tp/HX/A45wWQN15CYr9bFRmmRdYStSO5L65lCCZRF+kYiSKhm9alqbcdiVA==", + "version": "7.51.5", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.5.tgz", + "integrity": "sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==", "license": "MIT", "engines": { "node": ">=12.22.0" @@ -21776,6 +21944,21 @@ "moment": "^2.29.4" } }, + "node_modules/react-native-circular-progress-indicator": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-native-circular-progress-indicator/-/react-native-circular-progress-indicator-4.4.2.tgz", + "integrity": "sha512-BlgshzIIIk0TP/CZY+5oyRsHl2Sb2l6cK5tzfmrVETfn6pN8dhOKrV0i3Z6i0SM8wRgncdUUSRBpCPAexh7JoQ==", + "license": "MIT", + "dependencies": { + "react-native-redash": "*" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-native": ">=0.59.0", + "react-native-reanimated": ">=2.2.0", + "react-native-svg": ">=12.1.1" + } + }, "node_modules/react-native-elements": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.3.tgz", @@ -21899,6 +22082,23 @@ "react-native": "*" } }, + "node_modules/react-native-redash": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/react-native-redash/-/react-native-redash-18.1.3.tgz", + "integrity": "sha512-RKdmAjs87iwA8iD7UOQ11rlQL+tZh56O5+5cV1M37Jk+4uH1Fvs22G3Q5v/wGx+0ncwz3wMolLLCezONOodTlA==", + "license": "MIT", + "dependencies": { + "abs-svg-path": "^0.1.1", + "normalize-svg-path": "^1.0.1", + "parse-svg-path": "^0.1.2" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-gesture-handler": "*", + "react-native-reanimated": ">=2.0.0" + } + }, "node_modules/react-native-safe-area-context": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.1.tgz", @@ -23990,14 +24190,14 @@ } }, "node_modules/supabase/node_modules/glob": { - "version": "10.3.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", - "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", + "jackspeak": "^3.1.2", "minimatch": "^9.0.1", "minipass": "^7.0.4", "path-scurry": "^1.11.0" @@ -24180,6 +24380,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-arc-to-cubic-bezier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", + "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==", + "license": "ISC" + }, "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -24872,9 +25078,9 @@ } }, "node_modules/undici": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.17.0.tgz", - "integrity": "sha512-fs13QiDjPIzJ7gFAOal9CSG0c92rT2xw6MuMUJ4H30Eg5GCauLWYCCZA1tInjd6M4y+JZjVCCFr9pFpbhcC64w==", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.1.tgz", + "integrity": "sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==", "license": "MIT", "engines": { "node": ">=18.17" @@ -25714,10 +25920,13 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index 3a4fa04..4a68399 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@fortawesome/fontawesome-svg-core": "6.5.2", "@fortawesome/free-solid-svg-icons": "6.5.2", "@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-navigation/native": "6.1.17", "@supabase/supabase-js": "2.43.2", @@ -39,9 +39,10 @@ "lottie-react-native": "6.7.0", "react": "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-calendars": "1.1305.0", + "react-native-circular-progress-indicator": "4.4.2", "react-native-elements": "3.4.3", "react-native-gesture-handler": "2.16.2", "react-native-paper": "5.12.3", @@ -67,21 +68,21 @@ "@types/node": "20.12.12", "@types/react": "18.2.79", "@types/react-test-renderer": "18.3.0", - "@typescript-eslint/eslint-plugin": "7.9.0", - "@typescript-eslint/parser": "7.9.0", + "@typescript-eslint/eslint-plugin": "7.10.0", + "@typescript-eslint/parser": "7.10.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-promise": "6.1.1", "eslint-plugin-react": "7.34.1", "eslint-plugin-react-hooks": "4.6.2", "eslint-plugin-react-native": "4.1.0", - "eslint-plugin-unicorn": "51.0.1", + "eslint-plugin-unicorn": "53.0.0", "husky": "9.0.11", "jest": "29.7.0", "jest-expo": "51.0.2", "jest-junit": "16.0.0", - "lint-staged": "15.2.2", + "lint-staged": "15.2.4", "prettier": "3.2.5", "react-test-renderer": "18.2.0", "supabase": "1.167.4", diff --git a/presentation/react-native/components/HabitsMainPage/HabitsList.tsx b/presentation/react-native/components/HabitsMainPage/HabitsList.tsx index 28abb12..28c0671 100644 --- a/presentation/react-native/components/HabitsMainPage/HabitsList.tsx +++ b/presentation/react-native/components/HabitsMainPage/HabitsList.tsx @@ -6,7 +6,7 @@ import { Divider, List, Text } from "react-native-paper" import { GOAL_FREQUENCIES, type GoalFrequency } from "@/domain/entities/Goal" import type { HabitHistory } from "@/domain/entities/HabitHistory" 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 { HabitCard } from "./HabitCard" @@ -96,7 +96,7 @@ export const HabitsList: React.FC = (props) => { marginTop: 20, }} > - {selectedDate.toLocaleDateString("en-US", { + {selectedDate.toLocaleDateString(LOCALE, { weekday: "long", year: "numeric", month: "long", diff --git a/presentation/react-native/components/HabitsStatistics.tsx b/presentation/react-native/components/HabitsStatistics.tsx new file mode 100644 index 0000000..46ac600 --- /dev/null +++ b/presentation/react-native/components/HabitsStatistics.tsx @@ -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 = (props) => { + const { habitsTracker } = props + + const today = getNowDateUTC() + const todayISO = getISODate(today) + + const [selectedDate, setSelectedDate] = useState(today) + const selectedDateISO = getISODate(selectedDate) + + return ( + { + setSelectedDate(new Date(date.dateString)) + }} + markedDates={{ + [todayISO]: { marked: true, today: true }, + }} + maxDate={todayISO} + selected={selectedDateISO} + renderList={() => { + return ( + <> + + + + {selectedDate.toLocaleDateString(LOCALE, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + })} + + + {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 ( + + + + {totalGoalsSuccess} achieved goals in the{" "} + {GOAL_FREQUENCIES_TYPES[frequency]} out of{" "} + {totalGoals}. + + + + + ) + }, + )} + + ) + }} + /> + ) +} diff --git a/utils/__tests__/maths.test.ts b/utils/__tests__/maths.test.ts new file mode 100644 index 0000000..39cbcaa --- /dev/null +++ b/utils/__tests__/maths.test.ts @@ -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) + }) + }) +}) diff --git a/utils/maths.ts b/utils/maths.ts new file mode 100644 index 0000000..003966d --- /dev/null +++ b/utils/maths.ts @@ -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 +} diff --git a/utils/strings.ts b/utils/strings.ts index df75057..2f4dc53 100644 --- a/utils/strings.ts +++ b/utils/strings.ts @@ -1,3 +1,5 @@ +export const LOCALE = "en-US" + export const capitalize = (string: string): string => { return string.charAt(0).toUpperCase() + string.slice(1) }