Repository: jayvicsanantonio/tracknstick.com
Pull request #53: Foundational structure and logic separation
Author: login: jayvicsanantonio
Created: 2025-04-17T21:14:17Z | Merged: 2025-04-17T21:56:31Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced a new API layer and custom hook for managing habits, enabling seamless habit creation, updating, deletion, and completion toggling.
  - Added robust type definitions for habits, improving type safety and consistency throughout the app.

- **Refactor**
  - Refactored habit management logic to use the new API and custom hook, simplifying components and improving maintainability.

- **Chores**
  - Integrated automated code formatting and linting with pre-commit hooks to ensure consistent code quality.
  - Added a shared Axios instance with automatic authorization for secure API requests.

- **Documentation**
  - Enhanced type declarations for improved developer experience and integration with authentication services.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..2312dc5
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
diff --git a/package-lock.json b/package-lock.json
index 66fc622..9c4e6cd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -46,7 +46,10 @@
         "eslint-plugin-react-hooks": "^5.1.0-rc.0",
         "eslint-plugin-react-refresh": "^0.4.12",
         "globals": "^15.9.0",
+        "husky": "^9.1.7",
+        "lint-staged": "^15.5.1",
         "postcss": "^8.4.47",
+        "prettier": "^3.5.3",
         "tailwindcss": "^3.4.14",
         "typescript": "^5.5.3",
         "typescript-eslint": "^8.7.0",
@@ -3886,6 +3889,22 @@
         "url": "https://github.com/sponsors/epoberezkin"
       }
     },
+    "node_modules/ansi-escapes": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+      "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "environment": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/ansi-regex": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
@@ -3898,6 +3917,18 @@
         "url": "https://github.com/chalk/ansi-regex?sponsor=1"
       }
     },
+    "node_modules/ansi-styles": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
     "node_modules/any-promise": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@@ -4284,6 +4315,19 @@
       ],
       "license": "CC-BY-4.0"
     },
+    "node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
     "node_modules/chokidar": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -4341,6 +4385,64 @@
         "node": ">=6"
       }
     },
+    "node_modules/cli-cursor": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+      "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+      "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "slice-ansi": "^5.0.0",
+        "string-width": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate/node_modules/emoji-regex": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+      "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cli-truncate/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/clsx": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -4350,6 +4452,13 @@
         "node": ">=6"
       }
     },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -4593,9 +4702,9 @@
       }
     },
     "node_modules/debug": {
-      "version": "4.3.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
-      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -4737,6 +4846,19 @@
       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
       "license": "MIT"
     },
+    "node_modules/environment": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+      "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/es-abstract": {
       "version": "1.23.3",
       "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz",
@@ -5260,6 +5382,30 @@
       "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
       "license": "MIT"
     },
+    "node_modules/execa": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+      "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^8.0.1",
+        "human-signals": "^5.0.0",
+        "is-stream": "^3.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^5.1.0",
+        "onetime": "^6.0.0",
+        "signal-exit": "^4.1.0",
+        "strip-final-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=16.17"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
     "node_modules/fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -5553,6 +5699,19 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/get-east-asian-width": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+      "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-intrinsic": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@@ -5582,6 +5741,19 @@
         "node": ">=6"
       }
     },
+    "node_modules/get-stream": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+      "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-symbol-description": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
@@ -5789,6 +5961,32 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/human-signals": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+      "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=16.17.0"
+      }
+    },
+    "node_modules/husky": {
+      "version": "9.1.7",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+      "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "husky": "bin.js"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/typicode"
+      }
+    },
     "node_modules/ignore": {
       "version": "5.3.2",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -6141,6 +6339,19 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-string": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -6416,6 +6627,125 @@
       "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
       "license": "MIT"
     },
+    "node_modules/lint-staged": {
+      "version": "15.5.1",
+      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.1.tgz",
+      "integrity": "sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^5.4.1",
+        "commander": "^13.1.0",
+        "debug": "^4.4.0",
+        "execa": "^8.0.1",
+        "lilconfig": "^3.1.3",
+        "listr2": "^8.2.5",
+        "micromatch": "^4.0.8",
+        "pidtree": "^0.6.0",
+        "string-argv": "^0.3.2",
+        "yaml": "^2.7.0"
+      },
+      "bin": {
+        "lint-staged": "bin/lint-staged.js"
+      },
+      "engines": {
+        "node": ">=18.12.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/lint-staged"
+      }
+    },
+    "node_modules/lint-staged/node_modules/commander": {
+      "version": "13.1.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+      "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/lint-staged/node_modules/lilconfig": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+      "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antonk52"
+      }
+    },
+    "node_modules/listr2": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.2.tgz",
+      "integrity": "sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cli-truncate": "^4.0.0",
+        "colorette": "^2.0.20",
+        "eventemitter3": "^5.0.1",
+        "log-update": "^6.1.0",
+        "rfdc": "^1.4.1",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/listr2/node_modules/emoji-regex": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+      "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/listr2/node_modules/eventemitter3": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+      "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/listr2/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/listr2/node_modules/wrap-ansi": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+      "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -6445,6 +6775,102 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/log-update": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+      "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-escapes": "^7.0.0",
+        "cli-cursor": "^5.0.0",
+        "slice-ansi": "^7.1.0",
+        "strip-ansi": "^7.1.0",
+        "wrap-ansi": "^9.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/emoji-regex": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+      "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+      "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "get-east-asian-width": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/slice-ansi": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+      "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "is-fullwidth-code-point": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/string-width": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+      "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^10.3.0",
+        "get-east-asian-width": "^1.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/wrap-ansi": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+      "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.2.1",
+        "string-width": "^7.0.0",
+        "strip-ansi": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -6476,6 +6902,13 @@
         "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
       }
     },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/merge2": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -6519,6 +6952,32 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/mimic-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mimic-function": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+      "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -6625,6 +7084,35 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/npm-run-path": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+      "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/npm-run-path/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -6737,6 +7225,22 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/onetime": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.4",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -6871,6 +7375,19 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
+    "node_modules/pidtree": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+      "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "pidtree": "bin/pidtree.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
     "node_modules/pify": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@@ -7081,6 +7598,22 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/prettier": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
+      "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "prettier": "bin/prettier.cjs"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
     "node_modules/prop-types": {
       "version": "15.8.1",
       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -7399,6 +7932,39 @@
         "node": ">=4"
       }
     },
+    "node_modules/restore-cursor": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+      "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^7.0.0",
+        "signal-exit": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/restore-cursor/node_modules/onetime": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+      "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-function": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/reusify": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -7409,6 +7975,13 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/rollup": {
       "version": "4.40.0",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
@@ -7611,6 +8184,36 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/slice-ansi": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+      "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.0.0",
+        "is-fullwidth-code-point": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+      "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/source-map-js": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -7626,6 +8229,16 @@
       "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
       "license": "MIT"
     },
+    "node_modules/string-argv": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+      "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6.19"
+      }
+    },
     "node_modules/string-width": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -7812,6 +8425,19 @@
         "node": ">=8"
       }
     },
+    "node_modules/strip-final-newline": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -8577,18 +9203,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/wrap-ansi/node_modules/ansi-styles": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
-      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
-      }
-    },
     "node_modules/yallist": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -8597,9 +9211,9 @@
       "license": "ISC"
     },
     "node_modules/yaml": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz",
-      "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
+      "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
       "license": "ISC",
       "bin": {
         "yaml": "bin.mjs"
diff --git a/package.json b/package.json
index 25547f2..c929e13 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
     "dev": "vite",
     "build": "tsc -b && vite build",
     "lint": "eslint .",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "prepare": "husky"
   },
   "dependencies": {
     "@clerk/clerk-react": "^5.21.2",
@@ -48,10 +49,22 @@
     "eslint-plugin-react-hooks": "^5.1.0-rc.0",
     "eslint-plugin-react-refresh": "^0.4.12",
     "globals": "^15.9.0",
+    "husky": "^9.1.7",
+    "lint-staged": "^15.5.1",
     "postcss": "^8.4.47",
+    "prettier": "^3.5.3",
     "tailwindcss": "^3.4.14",
     "typescript": "^5.5.3",
     "typescript-eslint": "^8.7.0",
     "vite": "^6.2.6"
+  },
+  "lint-staged": {
+    "*.{js,jsx,ts,tsx}": [
+      "eslint --fix",
+      "prettier --write"
+    ],
+    "*.{json,md,css}": [
+      "prettier --write"
+    ]
   }
 }
diff --git a/src/App.tsx b/src/App.tsx
index 30a1003..22d7fc8 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,73 +1,26 @@
-import { useContext, useState } from 'react';
-import useSWR from 'swr';
-import { useToast } from '@/hooks/use-toast';
-import { useToggle } from '@/hooks/use-toggle';
-import { Habit } from '@/types/habit';
-import { ThemeContext } from '@/context/ThemeContext';
-import { DateContext } from '@/context/DateContext';
-import Header from '@/components/common/Header';
-import Body from '@/components/common/Body';
-import AddHabitDialog from '@/components/common/AddHabitDialog';
-import ProgressOverview from '@/components/common/ProgressOverview';
-import Footer from '@/components/common/Footer';
-import EditHabitDialog from '@/components/common/EditHabitDialog';
-import { useAuth } from '@clerk/clerk-react';
-import axios from 'axios';
-import getConfig from '@/lib/getConfig';
-const { apiHost } = getConfig();
+import { useContext, useState } from "react";
+import { useToggle } from "@/hooks/use-toggle";
+import { Habit } from "@/features/habits/types";
+import { ThemeContext } from "@/context/ThemeContext";
+import Header from "@/components/common/Header";
+import Body from "@/components/common/Body";
+import AddHabitDialog from "@/components/common/AddHabitDialog";
+import ProgressOverview from "@/components/common/ProgressOverview";
+import Footer from "@/components/common/Footer";
+import EditHabitDialog from "@/components/common/EditHabitDialog";
 
 function App() {
-  const { toast } = useToast();
-  const { getToken, isSignedIn } = useAuth();
   const { isDarkMode } = useContext(ThemeContext);
-  const { date, timeZone } = useContext(DateContext);
-  const habitsEndpointKey = isSignedIn
-    ? `/api/v1/habits?date=${date.toISOString()}&timeZone=${timeZone}`
-    : null;
-  const { data: habits = [], isLoading } = useSWR<Habit[]>(
-    habitsEndpointKey,
-    fetcher
-  );
-  const [editingHabit, setEditingHabit] = useState<Habit | null>(
-    null
-  );
+  const [editingHabit, setEditingHabit] = useState<Habit | null>(null);
   const [isEditMode, toggleIsEditMode] = useToggle(false);
-  const [showAddHabitDialog, toggleShowAddHabitDialog] =
-    useToggle(false);
-  const [showEditHabitDialog, toggleShowEditHabitDialog] =
-    useToggle(false);
+  const [showAddHabitDialog, toggleShowAddHabitDialog] = useToggle(false);
+  const [showEditHabitDialog, toggleShowEditHabitDialog] = useToggle(false);
   const [isOverviewMode, toggleIsOverviewMode] = useToggle(false);
 
-  async function fetcher(endpoint: string): Promise<Habit[]> {
-    try {
-      const token = await getToken();
-      const response = await axios.get<Habit[]>(
-        `${apiHost}${endpoint}`,
-        {
-          headers: {
-            Authorization: `Bearer ${token}`,
-          },
-        }
-      );
-      const data = response.data;
-
-      return data;
-    } catch (error) {
-      console.error('Error fetching data:', error);
-      toast({
-        description:
-          'Failed to fetch habits. Please reload the page.',
-      });
-      throw error;
-    }
-  }
-
-  if (isLoading) return <div>Loading...</div>;
-
   return (
     <div
       className={`min-h-screen ${
-        isDarkMode ? 'bg-gray-900 text-white' : 'bg-purple-100'
+        isDarkMode ? "bg-gray-900 text-white" : "bg-purple-100"
       } p-4 sm:p-8`}
     >
       <div className="min-w-[400px] max-w-7xl mx-auto flex flex-col min-h-screen">
@@ -77,9 +30,9 @@ function App() {
           toggleIsEditMode={toggleIsEditMode}
           toggleIsOverviewMode={toggleIsOverviewMode}
         />
+
         <Body
           isEditMode={isEditMode}
-          habits={habits}
           toggleShowAddHabitDialog={toggleShowAddHabitDialog}
           toggleShowEditHabitDialog={toggleShowEditHabitDialog}
           setEditingHabit={setEditingHabit}
diff --git a/src/components/common/Body.tsx b/src/components/common/Body.tsx
index 1021514..fe702ab 100644
--- a/src/components/common/Body.tsx
+++ b/src/components/common/Body.tsx
@@ -1,18 +1,16 @@
-import { SignedIn, SignedOut } from '@clerk/clerk-react';
+import { SignedIn, SignedOut } from "@clerk/clerk-react";
 
-import Welcome from '@/components/common/Welcome';
-import DailyHabitTracker from '@/components/common/DailyHabitTracker';
-import { Habit } from '@/types/habit';
+import Welcome from "@/components/common/Welcome";
+import DailyHabitTracker from "@/components/common/DailyHabitTracker";
+import { Habit } from "@/types/habit";
 
 export default function Body({
   isEditMode,
-  habits,
   toggleShowAddHabitDialog,
   toggleShowEditHabitDialog,
   setEditingHabit,
 }: {
   isEditMode: boolean;
-  habits: Habit[];
   toggleShowAddHabitDialog: () => void;
   toggleShowEditHabitDialog: () => void;
   setEditingHabit: (habit: Habit | null) => void;
@@ -25,7 +23,6 @@ export default function Body({
       <SignedIn>
         <DailyHabitTracker
           isEditMode={isEditMode}
-          habits={habits}
           toggleIsEditingHabit={toggleShowEditHabitDialog}
           setEditingHabit={setEditingHabit}
           onAddHabitClick={toggleShowAddHabitDialog}
diff --git a/src/components/common/DailyHabitTracker.tsx b/src/components/common/DailyHabitTracker.tsx
index 4324879..eec5a7e 100644
--- a/src/components/common/DailyHabitTracker.tsx
+++ b/src/components/common/DailyHabitTracker.tsx
@@ -1,50 +1,37 @@
-import {
-  useCallback,
-  useContext,
-  useEffect,
-  useMemo,
-  useState,
-} from 'react';
-import { useSWRConfig } from 'swr';
-import { Card, CardContent, CardHeader } from '@/components/ui/card';
-import { Habit } from '@/types/habit';
-import NoHabits from '@/components/common/NoHabits';
-import DailyHabitDate from '@/components/common/DailyHabitDate';
-import DailyHabitProgressIndicator from '@/components/common/DailyHabitProgressIndicator';
-import DailyHabitList from '@/components/common/DailyHabitList';
-import { ThemeContext } from '@/context/ThemeContext';
-import { DateContext } from '@/context/DateContext';
-import toggleOnSound from '@/assets/audio/habit-toggled-on.mp3';
-import toggleOffSound from '@/assets/audio/habit-toggled-off.mp3';
-import completedAllHabits from '@/assets/audio/completed-all-habits.mp3';
-import { useAuth } from '@clerk/clerk-react';
-import axios from 'axios';
-import getConfig from '@/lib/getConfig';
-const { apiHost } = getConfig();
+import { useContext, useEffect, useMemo, useState } from "react";
+import { Card, CardContent, CardHeader } from "@/components/ui/card";
+import { Habit } from "@/types/habit";
+import NoHabits from "@/components/common/NoHabits";
+import DailyHabitDate from "@/components/common/DailyHabitDate";
+import DailyHabitProgressIndicator from "@/components/common/DailyHabitProgressIndicator";
+import DailyHabitList from "@/components/common/DailyHabitList";
+import { ThemeContext } from "@/context/ThemeContext";
+import toggleOnSound from "@/assets/audio/habit-toggled-on.mp3";
+import toggleOffSound from "@/assets/audio/habit-toggled-off.mp3";
+import completedAllHabits from "@/assets/audio/completed-all-habits.mp3";
+import { useHabits } from "@/features/habits/hooks/useHabits";
 
 export default function DailyHabitTracker({
   isEditMode,
-  habits,
   toggleIsEditingHabit,
   setEditingHabit,
   onAddHabitClick,
 }: {
   isEditMode: boolean;
-  habits: Habit[];
   toggleIsEditingHabit: () => void;
   setEditingHabit: (habit: Habit | null) => void;
   onAddHabitClick: () => void;
 }) {
-  const { mutate } = useSWRConfig();
-  const { getToken } = useAuth();
   const { isDarkMode } = useContext(ThemeContext);
-  const { date, timeZone } = useContext(DateContext);
-  const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(
-    null
-  );
-  const [animatingHabitId, setAnimatingHabitId] = useState<
-    string | null
-  >(null);
+  const {
+    habits,
+    isLoading,
+    error,
+    toggleHabit: toggleHabitCompletion,
+  } = useHabits();
+
+  const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(null);
+  const [animatingHabitId, setAnimatingHabitId] = useState<string | null>(null);
 
   useEffect(() => {
     return () => {
@@ -55,92 +42,54 @@ export default function DailyHabitTracker({
   }, [timeoutId]);
 
   const completionRate = useMemo(() => {
-    const completedHabits = habits.filter(
-      (habit) => habit.completed
-    ).length;
-    return Math.round((completedHabits / habits.length) * 100);
+    const completedHabits = habits.filter((habit) => habit.completed).length;
+    const totalHabits = habits.length;
+    return totalHabits > 0
+      ? Math.round((completedHabits / totalHabits) * 100)
+      : 0;
   }, [habits]);
 
-  const addTracker = useCallback(
-    async (id: string): Promise<void> => {
-      const dateString = date.toISOString();
-
-      try {
-        const token = await getToken();
-        await axios.post<{
-          message: string;
-          trackerId: string;
-        }>(
-          `${apiHost}/api/v1/habits/${id}/trackers`,
-          {
-            timeZone,
-            timestamp: dateString,
-          },
-          {
-            headers: {
-              Authorization: `Bearer ${token}`,
-            },
-          }
-        );
-      } catch (error) {
-        console.error('Error adding tracker:', error);
-        throw error;
-      } finally {
-        await mutate(
-          `/api/v1/habits?date=${dateString}&timeZone=${timeZone}`,
-          habits.map((habit) => {
-            if (habit.id === id) {
-              const newCompleted = !habit.completed;
-
-              if (newCompleted) {
-                if (timeoutId) {
-                  clearTimeout(timeoutId);
-                }
-
-                setAnimatingHabitId(id);
-
-                const newTimeoutId = setTimeout(
-                  () => setAnimatingHabitId(null),
-                  1000
-                );
-
-                setTimeoutId(newTimeoutId);
-              }
-              return { ...habit, completed: newCompleted };
-            }
-            return habit;
-          })
-        );
+  const handleToggleHabit = async (id: string) => {
+    const habit = habits.find((h) => h.id === id);
+    if (habit && !habit.completed) {
+      if (timeoutId) {
+        clearTimeout(timeoutId);
       }
-    },
-    [habits, date, timeZone, timeoutId, mutate, getToken]
-  );
+      setAnimatingHabitId(id);
+      const newTimeoutId = setTimeout(() => setAnimatingHabitId(null), 1000);
+      setTimeoutId(newTimeoutId);
+    }
+
+    await toggleHabitCompletion(id);
 
-  const toggleHabit = async (id: string) => {
-    await addTracker(id);
-    const habit = habits.find((habit) => habit.id === id);
     const allHabitsCompleted =
-      habits.filter((habit) => habit.completed).length ===
-        habits.length - 1 && !habit?.completed;
+      habits.filter((h) => h.completed).length === habits.length - 1 &&
+      !habit?.completed;
 
     if (allHabitsCompleted) {
       const audio = new Audio(completedAllHabits);
       await audio.play();
     } else {
-      const audio = habit?.completed
-        ? new Audio(toggleOffSound)
-        : new Audio(toggleOnSound);
+      const audio = !habit?.completed
+        ? new Audio(toggleOnSound)
+        : new Audio(toggleOffSound);
       await audio.play();
     }
   };
 
+  if (isLoading) {
+    return <div>Loading habits...</div>;
+  }
+
+  if (error) {
+    return <div>Error loading habits. Please try again.</div>;
+  }
+
   return (
     <div className="flex-1">
       <Card
         className={`min-w-[400px] ${
-          isDarkMode
-            ? 'border-gray-700 bg-gray-800'
-            : 'border-purple-200'
+          isDarkMode ? "border-gray-700 bg-gray-800" : "border-purple-200"
         }
       `}
       >
@@ -152,13 +101,11 @@ export default function DailyHabitTracker({
             <NoHabits onAddHabitClick={onAddHabitClick} />
           ) : (
             <>
-              <DailyHabitProgressIndicator
-                completionRate={completionRate}
-              />
+              <DailyHabitProgressIndicator completionRate={completionRate} />
               <DailyHabitList
                 isEditMode={isEditMode}
                 habits={habits}
-                toggleHabit={toggleHabit}
+                toggleHabit={handleToggleHabit}
                 animatingHabitId={animatingHabitId}
                 toggleIsEditingHabit={toggleIsEditingHabit}
                 setEditingHabit={setEditingHabit}
diff --git a/src/features/habits/api/index.ts b/src/features/habits/api/index.ts
new file mode 100644
index 0000000..e4d0d11
--- /dev/null
+++ b/src/features/habits/api/index.ts
@@ -0,0 +1,59 @@
+import axiosInstance from "@/services/api/axiosInstance";
+import { Habit } from "@/features/habits/types";
+
+export const fetchHabits = async (
+  date: Date,
+  timeZone: string,
+): Promise<Habit[]> => {
+  const response = await axiosInstance.get<Habit[]>("/api/v1/habits", {
+    params: { date: date.toISOString(), timeZone },
+  });
+  return response.data;
+};
+
+export const addHabit = async (
+  habitData: Omit<Habit, "id" | "completed" | "stats">,
+): Promise<{ message: string; habitId: string }> => {
+  const response = await axiosInstance.post<{
+    message: string;
+    habitId: string;
+  }>("/api/v1/habits", habitData);
+  return response.data;
+};
+
+export const updateHabit = async (
+  habitId: string,
+  habitData: Partial<Omit<Habit, "id" | "completed" | "stats">>,
+): Promise<{ message: string; habitId: string }> => {
+  const response = await axiosInstance.put<{
+    message: string;
+    habitId: string;
+  }>(`/api/v1/habits/${habitId}`, habitData);
+  return response.data;
+};
+
+export const deleteHabit = async (
+  habitId: string,
+): Promise<{ message: string; habitId: string }> => {
+  const response = await axiosInstance.delete<{
+    message: string;
+    habitId: string;
+  }>(`/api/v1/habits/${habitId}`);
+  return response.data;
+};
+
+export const toggleHabitCompletion = async (
+  habitId: string,
+  date: Date,
+  timeZone: string,
+): Promise<{ message: string; trackerId?: string }> => {
+  const response = await axiosInstance.post<{
+    message: string;
+    trackerId?: string;
+  }>(`/api/v1/habits/${habitId}/trackers`, {
+    timestamp: date.toISOString(),
+    timeZone,
+  });
+
+  return response.data;
+};
diff --git a/src/features/habits/hooks/useHabits.ts b/src/features/habits/hooks/useHabits.ts
new file mode 100644
index 0000000..9f02170
--- /dev/null
+++ b/src/features/habits/hooks/useHabits.ts
@@ -0,0 +1,161 @@
+import { useCallback, useContext } from "react";
+import useSWR from "swr";
+import { useToast } from "@/hooks/use-toast";
+import { DateContext } from "@/context/DateContext";
+import { Habit } from "@/features/habits/types";
+import {
+  fetchHabits,
+  addHabit as apiAddHabit,
+  updateHabit as apiUpdateHabit,
+  deleteHabit as apiDeleteHabit,
+  toggleHabitCompletion as apiToggleHabitCompletion,
+} from "@/features/habits/api";
+
+interface UseHabitsReturn {
+  habits: Habit[];
+  isLoading: boolean;
+  error: unknown;
+  mutateHabits: () => Promise<Habit[] | undefined>;
+  addHabit: (
+    habitData: Omit<Habit, "id" | "completed" | "stats">,
+  ) => Promise<void>;
+  updateHabit: (
+    habitId: string,
+    habitData: Partial<Omit<Habit, "id" | "completed" | "stats">>,
+  ) => Promise<void>;
+  deleteHabit: (habitId: string, habitName: string) => Promise<void>;
+  toggleHabit: (habitId: string) => Promise<void>;
+}
+export function useHabits(): UseHabitsReturn {
+  const { toast } = useToast();
+  const { date, timeZone } = useContext(DateContext);
+
+  const habitsEndpointKey = date
+    ? `/api/v1/habits?date=${date.toISOString()}&timeZone=${timeZone}`
+    : null;
+
+  const {
+    data: fetchedHabits,
+    error,
+    isLoading,
+    mutate: mutateHabits,
+  } = useSWR<Habit[], unknown>(
+    habitsEndpointKey,
+    () => (date ? fetchHabits(date, timeZone) : Promise.resolve([])),
+    {},
+  );
+
+  const addHabit = useCallback(
+    async (habitData: Omit<Habit, "id" | "completed" | "stats">) => {
+      try {
+        await apiAddHabit(habitData);
+        toast({
+          description: `Habit "${habitData.name}" added successfully.`,
+        });
+        void mutateHabits();
+      } catch (err) {
+        console.error("Failed to add habit:", err);
+        toast({
+          variant: "destructive",
+          description: `Failed to add habit "${habitData.name}". Please try again.`,
+        });
+      }
+    },
+    [mutateHabits, toast],
+  );
+
+  const habits = fetchedHabits ?? [];
+
+  const updateHabit = useCallback(
+    async (
+      habitId: string,
+      habitData: Partial<Omit<Habit, "id" | "completed" | "stats">>,
+    ) => {
+      void mutateHabits(
+        (currentHabits) =>
+          currentHabits?.map((h) =>
+            h.id === habitId ? { ...h, ...habitData } : h,
+          ),
+        false,
+      );
+
+      try {
+        await apiUpdateHabit(habitId, habitData);
+        toast({
+          description: `Habit "${habitData.name ?? "habit"}" updated.`,
+        });
+      } catch (err) {
+        console.error("Failed to update habit:", err);
+        toast({
+          variant: "destructive",
+          description: `Failed to update habit "${
+            habitData.name ?? "habit"
+          }". Reverting changes.`,
+        });
+      } finally {
+        void mutateHabits();
+      }
+    },
+    [mutateHabits, toast],
+  );
+
+  const deleteHabit = useCallback(
+    async (habitId: string, habitName: string) => {
+      void mutateHabits(
+        (currentHabits) => currentHabits?.filter((h) => h.id !== habitId),
+        false,
+      );
+
+      try {
+        await apiDeleteHabit(habitId);
+        toast({ description: `Habit "${habitName}" deleted.` });
+      } catch (err) {
+        console.error("Failed to delete habit:", err);
+        toast({
+          variant: "destructive",
+          description: `Failed to delete habit "${habitName}". Restoring habit.`,
+        });
+      } finally {
+        void mutateHabits();
+      }
+    },
+    [mutateHabits, toast],
+  );
+
+  const toggleHabit = useCallback(
+    async (habitId: string) => {
+      void mutateHabits(
+        (currentHabits = []) =>
+          currentHabits.map((h) =>
+            h.id === habitId ? { ...h, completed: !h.completed } : h,
+          ),
+        false,
+      );
+
+      try {
+        if (!date) throw new Error("Date context is not available");
+        await apiToggleHabitCompletion(habitId, date, timeZone);
+      } catch (err) {
+        console.error("Failed to toggle habit completion:", err);
+        toast({
+          variant: "destructive",
+          description: "Failed to update habit status. Reverting change.",
+        });
+      } finally {
+        void mutateHabits();
+      }
+    },
+    [date, timeZone, mutateHabits, toast],
+  );
+
+  return {
+    habits,
+    isLoading,
+    error,
+    mutateHabits,
+    addHabit,
+    updateHabit,
+    deleteHabit,
+    toggleHabit,
+  };
+}
diff --git a/src/features/habits/types/index.ts b/src/features/habits/types/index.ts
new file mode 100644
index 0000000..c891ca8
--- /dev/null
+++ b/src/features/habits/types/index.ts
@@ -0,0 +1,16 @@
+export type Frequency = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun";
+
+import HabitsIcons from "@/icons/habits";
+
+export interface Habit {
+  id?: string;
+  name: string;
+  icon: keyof typeof HabitsIcons;
+  frequency: Frequency[];
+  completed: boolean;
+  stats: {
+    streak: number;
+    totalCompletions: number;
+    lastCompleted: string | null;
+  };
+}
diff --git a/src/services/api/axiosInstance.ts b/src/services/api/axiosInstance.ts
new file mode 100644
index 0000000..9ea09c5
--- /dev/null
+++ b/src/services/api/axiosInstance.ts
@@ -0,0 +1,32 @@
+import axios, { AxiosError } from "axios";
+import getConfig from "@/lib/getConfig";
+
+const { apiHost } = getConfig();
+
+const axiosInstance = axios.create({
+  baseURL: apiHost,
+});
+
+axiosInstance.interceptors.request.use(
+  async (config) => {
+    try {
+      if (window.Clerk?.session) {
+        const token = await window.Clerk.session.getToken();
+        if (token) {
+          config.headers.Authorization = `Bearer ${token}`;
+        }
+      } else {
+        console.warn("Clerk session not available for attaching token.");
+      }
+    } catch (error) {
+      console.error("Error getting Clerk token in interceptor:", error);
+    }
+    return config;
+  },
+  (error: AxiosError) => {
+    const errorMessage = `Request failed: ${error.message}`;
+    return Promise.reject(new Error(errorMessage));
+  },
+);
+
+export default axiosInstance;
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 11f02fe..c228710 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -1 +1,11 @@
 /// <reference types="vite/client" />
+
+interface ClerkInstance {
+  session?: {
+    getToken: (options?: { template?: string }) => Promise<string | null>;
+  };
+}
+
+interface Window {
+  Clerk?: ClerkInstance;
+}


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @jayvicsanantonio, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request introduces foundational structure and logic separation to the project. It incorporates Husky and lint-staged for pre-commit hooks, ensuring code quality and consistency. The changes include adding necessary dependencies, configuring linting and formatting tools, and refactoring the `App.tsx` component to improve separation of concerns by removing data fetching logic.

### Highlights
* **Husky and lint-staged Integration**: Implements Husky for Git hook management and lint-staged to run linters and formatters on staged files before committing, ensuring code quality and consistency.
* **Dependency Updates**: Adds new dev dependencies such as `husky`, `lint-staged`, and `prettier`, and updates existing dependencies in `package-lock.json` to support the new tooling.
* **Code Refactoring**: Refactors the `App.tsx` component to remove data fetching logic, promoting separation of concerns and preparing the codebase for better maintainability. The data fetching logic is moved to a custom hook `useHabits`.
* **API Abstraction**: Introduces an `axiosInstance` to abstract away the base URL and the token fetching logic, to reduce code duplication and improve maintainability.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **.husky/pre-commit**
  * Adds a pre-commit hook that runs `lint-staged`.
* **package-lock.json**
  * Updates dependencies to include `husky`, `lint-staged`, `prettier`, and their dependencies.
* **package.json**
  * Adds `prepare` script to run `husky` during installation.
  * Adds `husky`, `lint-staged`, and `prettier` as dev dependencies.
  * Configures `lint-staged` to run ESLint and Prettier on staged files.
* **src/App.tsx**
  * Removes data fetching logic from the `App` component.
  * Imports and uses the `useHabits` hook to manage habit data.
  * Removes unused imports and variables.
* **src/components/common/Body.tsx**
  * Removes the `habits` prop, as the data is now fetched within the `DailyHabitTracker` component.
* **src/components/common/DailyHabitTracker.tsx**
  * Removes data fetching logic and SWR configuration.
  * Imports and uses the `useHabits` hook to manage habit data.
  * Updates the `toggleHabit` function to use the `toggleHabitCompletion` function from the `useHabits` hook.
  * Adds loading and error handling.
* **src/features/habits/api/index.ts**
  * Creates a new directory and file to contain the API calls related to habits, including fetching, adding, updating, deleting, and toggling habit completion.
* **src/features/habits/hooks/useHabits.ts**
  * Creates a new directory and file to contain the `useHabits` hook, which manages the habit data and provides functions to interact with the API.
* **src/features/habits/types/index.ts**
  * Creates a new directory and file to contain the types related to habits.
* **src/services/api/axiosInstance.ts**
  * Creates a new directory and file to contain the `axiosInstance`, which is used to make API calls.
* **src/vite-env.d.ts**
  * Adds the `Clerk` interface to the `Window` interface, to allow the use of the Clerk SDK in TypeScript files.

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

> A hook to fetch,
> A state to hold,
> Logic now separate,
> Code's future is gold.


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T21:14:26Z
user: login: gemini-code-assist[bot]
body: ## Code Review
This pull request introduces foundational structure and logic separation, which is a great step towards improving the maintainability and scalability of the application. The introduction of linting and formatting tools will help ensure code quality and consistency. The refactoring of data fetching and state management into custom hooks improves the separation of concerns and makes the code more testable. Overall, these changes are positive and contribute to a more robust and well-organized codebase.

### Summary of Findings
* **Dependency Management**: The pull request introduces several new dependencies related to linting, formatting, and Git hooks. It's important to ensure these dependencies are necessary and don't introduce security vulnerabilities or performance issues.
* **Code Formatting and Linting**: The addition of Husky, lint-staged, and Prettier aims to enforce code formatting and linting rules. This is a good practice for maintaining code quality and consistency.
* **Data Fetching and State Management**: The original `App.tsx` fetched data directly. The updated code refactors this logic into a custom hook `useHabits`, improving separation of concerns and reusability.
* **Error Handling**: The original `App.tsx` had error handling within the `fetcher` function. The updated code centralizes error handling within the `useHabits` hook, providing a more consistent approach.

### Merge Readiness
The code changes are well-structured and improve the overall architecture of the application. However, it's important to carefully review the new dependencies and ensure they don't introduce any security or performance issues. I am unable to approve this pull request, and recommend that others review and approve this code before merging. Given the changes are mostly refactoring and dependency updates, and there are no critical or high severity issues, the pull request is likely ready to be merged once the dependencies are vetted.
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T21:14:40Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>src/services/api/axiosInstance.ts (1)</summary><blockquote>

`10-30`: **Well-implemented authentication interceptor**

The request interceptor effectively attaches the Clerk authentication token to all outgoing requests. The error handling is comprehensive, covering cases where the Clerk session is unavailable and when token retrieval fails.


Consider adding a response interceptor to handle common API response errors (like 401 Unauthorized, 403 Forbidden) consistently across the application:

```diff
+axiosInstance.interceptors.response.use(
+  (response) => response,
+  (error: AxiosError) => {
+    if (error.response) {
+      // Handle specific status codes
+      if (error.response.status === 401) {
+        console.error('Authentication error:', error.message);
+        // Potentially redirect to login or refresh token
+      } else if (error.response.status === 403) {
+        console.error('Authorization error:', error.message);
+      }
+    }
+    return Promise.reject(error);
+  }
+);
```

</blockquote></details>
<details>
<summary>src/components/common/DailyHabitTracker.tsx (1)</summary><blockquote>

`80-86`: **Enhance loading and error states**

While you've added basic loading and error states, consider enhancing them with:
- A skeleton UI during loading for better UX
- More specific error handling with retry options


```diff
-  if (isLoading) {
-    return <div>Loading habits...</div>;
-  }
+  if (isLoading) {
+    return (
+      <Card className={`min-w-[400px] ${isDarkMode ? "border-gray-700 bg-gray-800" : "border-purple-200"}`}>
+        <CardHeader>
+          <DailyHabitDate />
+        </CardHeader>
+        <CardContent className="animate-pulse">
+          {/* Skeleton UI for habits */}
+          <div className="h-4 w-3/4 bg-gray-200 rounded mb-4"></div>
+          <div className="h-12 bg-gray-200 rounded mb-3"></div>
+          <div className="h-12 bg-gray-200 rounded mb-3"></div>
+        </CardContent>
+      </Card>
+    );
+  }

-  if (error) {
-    return <div>Error loading habits. Please try again.</div>;
-  }
+  if (error) {
+    return (
+      <Card className={`min-w-[400px] ${isDarkMode ? "border-gray-700 bg-gray-800" : "border-purple-200"}`}>
+        <CardContent className="p-6">
+          <div className="text-center">
+            <p className="text-red-500 mb-3">Error loading habits.</p>
+            <button 
+              className="px-4 py-2 bg-purple-600 text-white rounded-md"
+              onClick={() => window.location.reload()}
+            >
+              Retry
+            </button>
+          </div>
+        </CardContent>
+      </Card>
+    );
+  }
```

</blockquote></details>
<details>
<summary>src/features/habits/hooks/useHabits.ts (1)</summary><blockquote>

`48-65`: **Consider handling edge cases in addHabit**

The addHabit function needs validation to ensure habitData is not empty or contains invalid values before making the API call.


```diff
  const addHabit = useCallback(
    async (habitData: Omit<Habit, "id" | "completed" | "stats">) => {
+     // Validate required fields
+     if (!habitData.name || habitData.name.trim() === '') {
+       toast({
+         variant: "destructive",
+         description: "Habit name cannot be empty",
+       });
+       return;
+     }
+
+     if (!habitData.frequency || habitData.frequency.length === 0) {
+       toast({
+         variant: "destructive",
+         description: "Please select at least one day for the habit frequency",
+       });
+       return;
+     }
+
      try {
        await apiAddHabit(habitData);
        toast({
          description: `Habit "${habitData.name}" added successfully.`,
        });
        void mutateHabits();
      } catch (err) {
        console.error("Failed to add habit:", err);
        toast({
          variant: "destructive",
          description: `Failed to add habit "${habitData.name}". Please try again.`,
        });
      }
    },
    [mutateHabits, toast],
  );
```

</blockquote></details>
<details>
<summary>src/features/habits/api/index.ts (1)</summary><blockquote>

`45-59`: **Clarify the purpose of toggleHabitCompletion**

The function name `toggleHabitCompletion` suggests toggling a boolean value, but it's actually creating or deleting a "tracker" record. Consider renaming or adding documentation to clarify this behavior.


```diff
/**
+ * Toggles habit completion status by creating or deleting a tracker record
+ * for the specified habit on the given date.
+ * 
+ * @param habitId - The ID of the habit to toggle
+ * @param date - The date for which to toggle completion
+ * @param timeZone - The timezone to use for date calculations
+ * @returns Promise with a message and optional tracker ID
+ */
export const toggleHabitCompletion = async (
  habitId: string,
  date: Date,
  timeZone: string,
): Promise<{ message: string; trackerId?: string }> => {
  const response = await axiosInstance.post<{
    message: string;
    trackerId?: string;
  }>(`/api/v1/habits/${habitId}/trackers`, {
    timestamp: date.toISOString(),
    timeZone,
  });

  return response.data;
};
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 72ad4e6d115993fc33617cb6a12821ab155cbe66 and 2780e5524436695515c40f9407dfa1b2b0fa1c99.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (10)</summary>

* `.husky/pre-commit` (1 hunks)
* `package.json` (2 hunks)
* `src/App.tsx` (2 hunks)
* `src/components/common/Body.tsx` (1 hunks)
* `src/components/common/DailyHabitTracker.tsx` (3 hunks)
* `src/features/habits/api/index.ts` (1 hunks)
* `src/features/habits/hooks/useHabits.ts` (1 hunks)
* `src/features/habits/types/index.ts` (1 hunks)
* `src/services/api/axiosInstance.ts` (1 hunks)
* `src/vite-env.d.ts` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (4)</summary>

<details>
<summary>src/services/api/axiosInstance.ts (1)</summary><blockquote>

<details>
<summary>src/lib/getConfig.ts (1)</summary>

* `getConfig` (1-11)

</details>

</blockquote></details>
<details>
<summary>src/App.tsx (3)</summary><blockquote>

<details>
<summary>src/context/ThemeContext.tsx (1)</summary>

* `ThemeContext` (8-11)

</details>
<details>
<summary>src/features/habits/types/index.ts (1)</summary>

* `Habit` (5-16)

</details>
<details>
<summary>src/hooks/use-toggle.ts (1)</summary>

* `useToggle` (3-7)

</details>

</blockquote></details>
<details>
<summary>src/features/habits/hooks/useHabits.ts (4)</summary><blockquote>

<details>
<summary>src/features/habits/types/index.ts (1)</summary>

* `Habit` (5-16)

</details>
<details>
<summary>src/hooks/use-toast.ts (2)</summary>

* `useToast` (194-194)
* `toast` (194-194)

</details>
<details>
<summary>src/context/DateContext.tsx (1)</summary>

* `DateContext` (13-21)

</details>
<details>
<summary>src/features/habits/api/index.ts (4)</summary>

* `fetchHabits` (4-12)
* `addHabit` (14-22)
* `updateHabit` (24-33)
* `deleteHabit` (35-43)

</details>

</blockquote></details>
<details>
<summary>src/features/habits/api/index.ts (1)</summary><blockquote>

<details>
<summary>src/features/habits/types/index.ts (1)</summary>

* `Habit` (5-16)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (20)</summary><blockquote>

<details>
<summary>package.json (2)</summary>

`52-55`: **DevDependencies additions look good**  
Adding `husky`, `lint-staged`, and `prettier` is appropriate for pre-commit linting and formatting workflows. Version ranges align with current stable releases.

---

`61-68`: **Lint-staged configuration is properly defined**  
The patterns and commands for JS/TS and JSON/MD/CSS files match the intended formatting and linting rules.

</details>
<details>
<summary>src/vite-env.d.ts (2)</summary>

`3-7`: **Well-defined ClerkInstance interface**

The `ClerkInstance` interface properly defines the structure of the Clerk authentication object, specifically focusing on the session's `getToken` method that will be used for API authorization.

---

`9-11`: **Appropriate Window interface extension**

Good approach extending the global `Window` interface to include the Clerk instance. This provides proper TypeScript type safety when accessing `window.Clerk` throughout the application.

</details>
<details>
<summary>src/components/common/Body.tsx (2)</summary>

`1-5`: **Clean import statements**

The updated import statements using the `@` alias paths are consistent with modern React practices, and correctly import the necessary components and types.

---

`24-30`: **Successfully refactored component props**

The `DailyHabitTracker` component no longer receives habits through props, which aligns with the architectural change to use the custom `useHabits` hook for data management instead of prop drilling.

</details>
<details>
<summary>src/services/api/axiosInstance.ts (1)</summary>

`1-8`: **Good Axios instance setup**

The Axios instance is correctly configured with the API host from the config. This centralizes API configuration and follows the best practice of using environment variables for configuration.

</details>
<details>
<summary>src/App.tsx (4)</summary>

`1-10`: **Clean import organization**

The imports have been correctly updated to reflect the architectural changes, removing unnecessary imports related to authentication and data fetching while maintaining the relevant UI component imports.

---

`14-17`: **Simplified state management**

The component now focuses only on UI state management with clean separation of concerns. The habit data fetching and manipulation logic has been successfully moved out of the component.

---

`33-39`: **Proper props passing to Body component**

The `Body` component now receives only the necessary UI state props without any data-related props, which aligns with the new architecture where data is managed through hooks.

---

`42-54`: **Modal components well integrated**

The dialog components for adding and editing habits, as well as the progress overview, are properly configured with the relevant UI state props. The component correctly manages which modal should be displayed and passes the necessary data.

</details>
<details>
<summary>src/features/habits/types/index.ts (2)</summary>

`1-1`: **Good type definition for frequency**

The `Frequency` type using string literal unions is an excellent choice for representing days of the week, providing strong type checking and auto-completion.

---

`5-16`: **Well-structured Habit interface**

The Habit interface is comprehensive and well-organized, with proper typing for all properties including the nested stats object. The use of `keyof typeof HabitsIcons` for the icon property ensures only valid icons can be used.

</details>
<details>
<summary>src/components/common/DailyHabitTracker.tsx (2)</summary>

`26-31`: **Good implementation of the useHabits hook**

The destructuring of values from the useHabits hook provides a clean way to access habits data and associated functions. This is a significant improvement over prop drilling.

---

`52-78`: **Well-implemented toggle handler with animation and sound feedback**

The `handleToggleHabit` function is well-structured with:
- Animation handling for visual feedback
- Proper async/await pattern
- Conditional sound feedback based on completion state
- Special handling for when all habits are completed

This creates a rich user experience with both visual and audio cues.

</details>
<details>
<summary>src/features/habits/hooks/useHabits.ts (3)</summary>

`14-28`: **Well-defined hook interface**

The TypeScript interface for `UseHabitsReturn` provides clear documentation of what the hook returns, making it easier for other developers to understand and use this hook.

---

`33-46`: **Effective data fetching setup with SWR**

Good use of useSWR with a key that includes both date and timeZone parameters, ensuring proper cache invalidation when these values change.

---

`125-149`: <details>
<summary>❌ Incorrect review comment</summary>

**Verify date context availability before using it**

In the toggleHabit function, you're checking if date is available but not timeZone, which is also used in the API call.


```diff
  const toggleHabit = useCallback(
    async (habitId: string) => {
      void mutateHabits(
        (currentHabits = []) =>
          currentHabits.map((h) =>
            h.id === habitId ? { ...h, completed: !h.completed } : h,
          ),
        false,
      );

      try {
-       if (!date) throw new Error("Date context is not available");
+       if (!date || !timeZone) throw new Error("Date or timezone is not available");
        await apiToggleHabitCompletion(habitId, date, timeZone);
      } catch (err) {
        console.error("Failed to toggle habit completion:", err);
        toast({
          variant: "destructive",
          description: "Failed to update habit status. Reverting change.",
        });
      } finally {
        void mutateHabits();
      }
    },
    [date, timeZone, mutateHabits, toast],
  );
```

Let's check if there might be places where timeZone could be undefined:

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for DateContext usage to verify how timeZone is initialized
rg -i -A 5 -B 5 "timeZone" --type ts --type tsx
```

Length of output: 75

---

To verify how `timeZone` is provided, let’s inspect the top of `useHabits.ts`.

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate and print the first 200 lines of useHabits.ts to see how date/timeZone are initialized
file=$(fd useHabits.ts)
echo "Found file at: $file"
head -n 200 "$file"
```

Length of output: 4597

---

To confirm whether `timeZone` can ever be undefined in this hook, let’s inspect your `DateContext` provider:

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate and print the top of DateContext to see its default values and exports
file=$(fd DateContext.tsx)
echo "Found DateContext at: $file"
head -n 200 "$file"
```

Length of output: 703

---



</details>

**No need to guard `timeZone`**  

The `DateContext` default value always provides a non‑empty `timeZone` string (see `src/context/DateContext.tsx`), so adding a runtime check for `timeZone` in `toggleHabit` isn’t necessary. You can safely keep the existing `if (!date)` guard—or remove it entirely if you prefer, since the context also defaults `date` to `new Date()`.  



> Likely an incorrect or invalid review comment.

</details>
<details>
<summary>src/features/habits/api/index.ts (2)</summary>

`4-12`: **Well-structured API function with proper types**

The fetchHabits function is well-typed and follows best practices for API calls, using axios and proper parameter passing.

---

`14-43`: **Consistent CRUD operations with appropriate type safety**

The addHabit, updateHabit, and deleteHabit functions follow a consistent pattern:
- Well-defined parameter types using TypeScript's utility types
- Proper return type definitions
- RESTful endpoint usage
- Consistent response structure

This consistency makes the API easy to understand and maintain.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-17T21:17:53Z

Review comments:
diff_hunk: @@ -25,7 +23,6 @@ export default function Body({
       <SignedIn>
         <DailyHabitTracker
           isEditMode={isEditMode}
-          habits={habits}
           toggleIsEditingHabit={toggleShowEditHabitDialog}
           setEditingHabit={setEditingHabit}
           onAddHabitClick={toggleShowAddHabitDialog}
path: src/components/common/Body.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `DailyHabitTracker` component is now responsible for fetching and managing habit data. This is a good separation of concerns, as it isolates the data fetching logic to a specific component. Consider renaming `toggleIsEditingHabit` to `toggleShowEditHabitDialog` for clarity.
created_at: 2025-04-17T21:14:40Z
author_association: NONE
diff_hunk: @@ -55,92 +42,54 @@ export default function DailyHabitTracker({
   }, [timeoutId]);
 
   const completionRate = useMemo(() => {
-    const completedHabits = habits.filter(
-      (habit) => habit.completed
-    ).length;
-    return Math.round((completedHabits / habits.length) * 100);
+    const completedHabits = habits.filter((habit) => habit.completed).length;
+    const totalHabits = habits.length;
+    return totalHabits > 0
+      ? Math.round((completedHabits / totalHabits) * 100)
+      : 0;
   }, [habits]);
 
-  const addTracker = useCallback(
-    async (id: string): Promise<void> => {
-      const dateString = date.toISOString();
-
-      try {
-        const token = await getToken();
-        await axios.post<{
-          message: string;
-          trackerId: string;
-        }>(
-          `${apiHost}/api/v1/habits/${id}/trackers`,
-          {
-            timeZone,
-            timestamp: dateString,
-          },
-          {
-            headers: {
-              Authorization: `Bearer ${token}`,
-            },
-          }
-        );
-      } catch (error) {
-        console.error('Error adding tracker:', error);
-        throw error;
-      } finally {
-        await mutate(
-          `/api/v1/habits?date=${dateString}&timeZone=${timeZone}`,
-          habits.map((habit) => {
-            if (habit.id === id) {
-              const newCompleted = !habit.completed;
-
-              if (newCompleted) {
-                if (timeoutId) {
-                  clearTimeout(timeoutId);
-                }
-
-                setAnimatingHabitId(id);
-
-                const newTimeoutId = setTimeout(
-                  () => setAnimatingHabitId(null),
-                  1000
-                );
-
-                setTimeoutId(newTimeoutId);
-              }
-              return { ...habit, completed: newCompleted };
-            }
-            return habit;
-          })
-        );
+  const handleToggleHabit = async (id: string) => {
+    const habit = habits.find((h) => h.id === id);
+    if (habit && !habit.completed) {
+      if (timeoutId) {
+        clearTimeout(timeoutId);
       }
-    },
-    [habits, date, timeZone, timeoutId, mutate, getToken]
-  );
+      setAnimatingHabitId(id);
+      const newTimeoutId = setTimeout(() => setAnimatingHabitId(null), 1000);
+      setTimeoutId(newTimeoutId);
+    }
+
+    await toggleHabitCompletion(id);
 
-  const toggleHabit = async (id: string) => {
-    await addTracker(id);
-    const habit = habits.find((habit) => habit.id === id);
     const allHabitsCompleted =
-      habits.filter((habit) => habit.completed).length ===
-        habits.length - 1 && !habit?.completed;
+      habits.filter((h) => h.completed).length === habits.length - 1 &&
+      !habit?.completed;
 
     if (allHabitsCompleted) {
       const audio = new Audio(completedAllHabits);
       await audio.play();
     } else {
-      const audio = habit?.completed
-        ? new Audio(toggleOffSound)
-        : new Audio(toggleOnSound);
+      const audio = !habit?.completed
+        ? new Audio(toggleOnSound)
+        : new Audio(toggleOffSound);
       await audio.play();
     }
   };
 
+  if (isLoading) {
+    return <div>Loading habits...</div>;
+  }
+
+  if (error) {
+    return <div>Error loading habits. Please try again.</div>;
+  }
path: src/components/common/DailyHabitTracker.tsx
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

These loading and error states are important for providing feedback to the user while the data is being fetched. Consider adding a more informative error message or a retry button.
created_at: 2025-04-17T21:14:40Z
author_association: NONE
diff_hunk: @@ -0,0 +1 @@
+npx lint-staged
path: .husky/pre-commit
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add Husky boilerplate (shebang and hook sourcing)**  
Shell scripts in `.husky` require a shebang and Husky runtime sourcing to work reliably across environments.  
  
Apply this diff:
```diff
+#!/usr/bin/env sh
+. "$(dirname "$0")/_/husky.sh"
+
 npx lint-staged
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-17T21:17:52Z
author_association: NONE
diff_hunk: @@ -7,7 +7,8 @@
     "dev": "vite",
     "build": "tsc -b && vite build",
     "lint": "eslint .",
-    "preview": "vite preview"
+    "preview": "vite preview",
+    "prepare": "husky"
path: package.json
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix the `prepare` script to correctly install Husky hooks**  
The `prepare` script should invoke `husky install` (not just `husky`) so that hooks are set up automatically after `npm install`.  
  
Apply this diff:
```diff
-    "prepare": "husky"
+    "prepare": "husky install"
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    "preview": "vite preview",
    "prepare": "husky install"
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-17T21:17:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,161 @@
+import { useCallback, useContext } from "react";
+import useSWR from "swr";
+import { useToast } from "@/hooks/use-toast";
+import { DateContext } from "@/context/DateContext";
+import { Habit } from "@/features/habits/types";
+import {
+  fetchHabits,
+  addHabit as apiAddHabit,
+  updateHabit as apiUpdateHabit,
+  deleteHabit as apiDeleteHabit,
+  toggleHabitCompletion as apiToggleHabitCompletion,
+} from "@/features/habits/api";
+
+interface UseHabitsReturn {
+  habits: Habit[];
+  isLoading: boolean;
+  error: unknown;
+  mutateHabits: () => Promise<Habit[] | undefined>;
+  addHabit: (
+    habitData: Omit<Habit, "id" | "completed" | "stats">,
+  ) => Promise<void>;
+  updateHabit: (
+    habitId: string,
+    habitData: Partial<Omit<Habit, "id" | "completed" | "stats">>,
+  ) => Promise<void>;
+  deleteHabit: (habitId: string, habitName: string) => Promise<void>;
+  toggleHabit: (habitId: string) => Promise<void>;
+}
+export function useHabits(): UseHabitsReturn {
+  const { toast } = useToast();
+  const { date, timeZone } = useContext(DateContext);
+
+  const habitsEndpointKey = date
+    ? `/api/v1/habits?date=${date.toISOString()}&timeZone=${timeZone}`
+    : null;
+
+  const {
+    data: fetchedHabits,
+    error,
+    isLoading,
+    mutate: mutateHabits,
+  } = useSWR<Habit[], unknown>(
+    habitsEndpointKey,
+    () => (date ? fetchHabits(date, timeZone) : Promise.resolve([])),
+    {},
+  );
+
+  const addHabit = useCallback(
+    async (habitData: Omit<Habit, "id" | "completed" | "stats">) => {
+      try {
+        await apiAddHabit(habitData);
+        toast({
+          description: `Habit "${habitData.name}" added successfully.`,
+        });
+        void mutateHabits();
+      } catch (err) {
+        console.error("Failed to add habit:", err);
+        toast({
+          variant: "destructive",
+          description: `Failed to add habit "${habitData.name}". Please try again.`,
+        });
+      }
+    },
+    [mutateHabits, toast],
+  );
+
+  const habits = fetchedHabits ?? [];
+
+  const updateHabit = useCallback(
+    async (
+      habitId: string,
+      habitData: Partial<Omit<Habit, "id" | "completed" | "stats">>,
+    ) => {
+      void mutateHabits(
+        (currentHabits) =>
+          currentHabits?.map((h) =>
+            h.id === habitId ? { ...h, ...habitData } : h,
+          ),
+        false,
+      );
+
+      try {
+        await apiUpdateHabit(habitId, habitData);
+        toast({
+          description: `Habit "${habitData.name ?? "habit"}" updated.`,
+        });
+      } catch (err) {
+        console.error("Failed to update habit:", err);
+        toast({
+          variant: "destructive",
+          description: `Failed to update habit "${
+            habitData.name ?? "habit"
+          }". Reverting changes.`,
+        });
+      } finally {
+        void mutateHabits();
+      }
+    },
+    [mutateHabits, toast],
+  );
+
+  const deleteHabit = useCallback(
+    async (habitId: string, habitName: string) => {
+      void mutateHabits(
+        (currentHabits) => currentHabits?.filter((h) => h.id !== habitId),
+        false,
+      );
+
+      try {
+        await apiDeleteHabit(habitId);
+        toast({ description: `Habit "${habitName}" deleted.` });
+      } catch (err) {
+        console.error("Failed to delete habit:", err);
+        toast({
+          variant: "destructive",
+          description: `Failed to delete habit "${habitName}". Restoring habit.`,
+        });
+      } finally {
+        void mutateHabits();
+      }
+    },
+    [mutateHabits, toast],
+  );
path: src/features/habits/hooks/useHabits.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add confirmation for habit deletion**

Consider adding a confirmation step in the UI before calling the deleteHabit function to prevent accidental deletions.


This would be implemented in the component that calls this function, but the function itself should document this expectation:

```diff
  const deleteHabit = useCallback(
    async (habitId: string, habitName: string) => {
+     // Note: UI components should confirm deletion with the user before calling this function
      void mutateHabits(
        (currentHabits) => currentHabits?.filter((h) => h.id !== habitId),
        false,
      );

      try {
        await apiDeleteHabit(habitId);
        toast({ description: `Habit "${habitName}" deleted.` });
      } catch (err) {
        console.error("Failed to delete habit:", err);
        toast({
          variant: "destructive",
          description: `Failed to delete habit "${habitName}". Restoring habit.`,
        });
      } finally {
        void mutateHabits();
      }
    },
    [mutateHabits, toast],
  );
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  const deleteHabit = useCallback(
    async (habitId: string, habitName: string) => {
      // Note: UI components should confirm deletion with the user before calling this function
      void mutateHabits(
        (currentHabits) => currentHabits?.filter((h) => h.id !== habitId),
        false,
      );

      try {
        await apiDeleteHabit(habitId);
        toast({ description: `Habit "${habitName}" deleted.` });
      } catch (err) {
        console.error("Failed to delete habit:", err);
        toast({
          variant: "destructive",
          description: `Failed to delete habit "${habitName}". Restoring habit.`,
        });
      } finally {
        void mutateHabits();
      }
    },
    [mutateHabits, toast],
  );
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-17T21:17:52Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-17T21:14:23Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces a modular and centralized approach to habit management and API interaction in the codebase. It adds a dedicated API layer for habit operations, a custom React hook (`useHabits`) for managing habit data and actions, and a shared Axios instance that automatically attaches authentication tokens to outgoing requests. The `App` and habit-related components are refactored to remove direct data fetching, authentication, and state logic, instead relying on the new hook. TypeScript interfaces and global declarations are added for strong typing and Clerk session access. Husky and lint-staged are integrated for code quality enforcement on commits.

## Changes

| File(s)                                                                                     | Change Summary                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
|---------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| .husky/pre-commit, package.json                                                             | Added Husky pre-commit hook to run lint-staged; updated `package.json` to include Husky, lint-staged, and Prettier as devDependencies; added lint-staged configuration for linting/formatting staged files; introduced a `prepare` script for Husky setup.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| src/App.tsx                                                                                 | Removed all authentication, data fetching, and habit state management logic; eliminated use of `useSWR`, `useAuth`, error toasts, and dynamic API endpoint construction; the component now only manages UI state toggles and editing state.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| src/components/common/Body.tsx                                                              | Removed the `habits` prop from the `Body` component and its invocation of `DailyHabitTracker`; updated function signature and type declarations accordingly; minor formatting changes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| src/components/common/DailyHabitTracker.tsx                                                 | Refactored to use the new `useHabits` hook for all habit data and mutation logic; removed direct API calls and token handling; simplified animation and feedback logic; added loading and error UI states; removed the `habits` prop from the component interface.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| src/features/habits/api/index.ts                                                            | Introduced a new API module exporting functions for fetching, adding, updating, deleting, and toggling habits via HTTP requests using a shared Axios instance; all functions are typed and return relevant data.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| src/features/habits/hooks/useHabits.ts                                                      | Added a custom React hook `useHabits` to manage habit data, loading/error states, and provide functions to add, update, delete, and toggle habits; uses SWR for data fetching and caching; handles optimistic updates and error toasts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| src/features/habits/types/index.ts                                                          | Added new TypeScript types: `Frequency` (days of week), and `Habit` interface describing habit object structure including stats and completion state.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| src/services/api/axiosInstance.ts                                                           | Added a shared Axios instance with base URL from config and a request interceptor that attaches Clerk authentication tokens to outgoing requests if available; exported as default.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| src/vite-env.d.ts                                                                           | Added global TypeScript declarations for Clerk session access on the `window` object, including the `ClerkInstance` interface and extending the global `Window` interface.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Component as DailyHabitTracker
    participant Hook as useHabits
    participant API as Habits API (axiosInstance)
    participant Clerk as Clerk Session

    Component->>Hook: Call useHabits()
    Hook->>API: fetchHabits(date, timeZone)
    API->>Clerk: Get token via window.Clerk.session.getToken()
    Clerk-->>API: Return token
    API-->>Hook: Return habits data
    Hook-->>Component: Return { habits, isLoading, error, addHabit, updateHabit, deleteHabit, toggleHabit }

    Component->>Hook: Call addHabit/updateHabit/deleteHabit/toggleHabit
    Hook->>API: Perform corresponding API request (with token)
    API-->>Hook: Return result
    Hook-->>Component: Trigger UI update/mutate data
```

## Possibly related PRs

- [jayvicsanantonio/tracknstick.com#50](https://github.com/jayvicsanantonio/tracknstick.com/pull/50): Also modifies `src/App.tsx` for habit data fetching and authentication logic, making it directly related to the changes here.
- [jayvicsanantonio/tracknstick.com#49](https://github.com/jayvicsanantonio/tracknstick.com/pull/49): Refactors components to replace a custom API client with direct axios calls and explicit token fetching, which relates to this PR's move to a centralized axios instance with automatic token handling.

## Poem

> In the garden of code, a new path appears,  
> With hooks and APIs, we banish old fears.  
> Habits now flourish, their logic pristine,  
> Axios brings tokens, unseen but keen.  
> Linting and Husky keep our carrots in line—  
> Hopping ahead, our codebase will shine!  
> 🥕✨

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=jayvicsanantonio/tracknstick.com&utm_content=53):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: cloudflare-workers-and-pages[bot]
created_at: 2025-04-17T21:14:26Z
body: ## Deploying tracknstick-com with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>2780e55</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://caeb1490.tracknstick-com.pages.dev'>https://caeb1490.tracknstick-com.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://foundational-structure-and-l.tracknstick-com.pages.dev'>https://foundational-structure-and-l.tracknstick-com.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/3ff5eddc22ab1009a4e16bd8f56015cb/pages/view/tracknstick-com/caeb1490-a46b-4ede-aca1-931bc8881ca9)

author_association: NONE
user: login: socket-security[bot]
created_at: 2025-04-17T21:14:39Z
body: **Review the following changes in direct dependencies.** Learn more about [Socket for GitHub](https://socket.dev?utm_medium=gh).

<table>
<thead>
<tr>
<th>Diff</th>
<th width="200px">Package</th>
<th align="left" width="100px">Supply Chain<br/>Security</th>
<th align="left" width="100px">Vulnerability</th>
<th align="left" width="100px">Quality</th>
<th align="left" width="100px">Maintenance</th>
<th align="left" width="100px">License</th>
</tr>
</thead>
<tbody>
<tr><td align="center"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302"><img src="https://github-app-statics.socket.dev/diff-added.svg" title="Added" alt="Added" width="20" height="20"></a></td><td><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302">husky@​9.1.7</a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Supply Chain Security" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Vulnerability" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302"><img src="https://github-app-statics.socket.dev/score-59.svg" title="Quality" width="40" height="40" alt="59"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302"><img src="https://github-app-statics.socket.dev/score-82.svg" title="Maintenance" width="40" height="40" alt="82"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15914414302"><img src="https://github-app-statics.socket.dev/score-100.svg" title="License" width="40" height="40" alt="100"></a></td></tr>
<tr><td align="center"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999"><img src="https://github-app-statics.socket.dev/diff-added.svg" title="Added" alt="Added" width="20" height="20"></a></td><td><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999">lint-staged@​15.5.1</a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999"><img src="https://github-app-statics.socket.dev/score-99.svg" title="Supply Chain Security" width="40" height="40" alt="99"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Vulnerability" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Quality" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999"><img src="https://github-app-statics.socket.dev/score-91.svg" title="Maintenance" width="40" height="40" alt="91"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=22142991999"><img src="https://github-app-statics.socket.dev/score-100.svg" title="License" width="40" height="40" alt="100"></a></td></tr>
<tr><td align="center"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361"><img src="https://github-app-statics.socket.dev/diff-added.svg" title="Added" alt="Added" width="20" height="20"></a></td><td><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361">prettier@​3.5.3</a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361"><img src="https://github-app-statics.socket.dev/score-98.svg" title="Supply Chain Security" width="40" height="40" alt="98"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Vulnerability" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Quality" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361"><img src="https://github-app-statics.socket.dev/score-94.svg" title="Maintenance" width="40" height="40" alt="94"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies&dependency_item_key=15957303361"><img src="https://github-app-statics.socket.dev/score-100.svg" title="License" width="40" height="40" alt="100"></a></td></tr>
</tbody>
</table>

[View full report](https://socket.dev/dashboard/org/jayvicsanantonio/diff/4f06ee21-b1a0-410f-89cc-db2f0222f1a2/27c6d291-da3b-4f9b-863d-a992b06ea275?tab=dependencies)

<!-- overview-comment -->
author_association: NONE