Repository: jjmbeba/t3-glucotracker
Pull request #7: Implement glucose log page
Author: login: jjmbeba
Created: 2025-05-13T09:58:48Z | Merged: 2025-05-13T11:03:32Z



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

- **New Features**
  - Introduced interactive charts for glucose logs: history, distribution, and pie charts to visualize glucose data over time.
  - Added filter controls for glucose logs with options for all, last week, and last month, plus clear filter functionality.
  - Added new badge and dialog UI components for enhanced styling and modal interactions.
  - Added a new glucose logs page with prefetching and client-side hydration for improved user experience.

- **Improvements**
  - Updated glucose log form for improved responsiveness and updated button labeling.
  - Enabled client-side hydration and optimized loading states for glucose log history display.

- **Bug Fixes**
  - Improved date handling in the glucose log form to prevent errors from invalid or missing dates.

- **Chores**
  - Added the "recharts" library for chart visualizations.
  - Added support for a new client-side environment variable for application URL configuration.
  - Added utility to remove query parameters from URLs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/package.json b/package.json
index ec063f0..3012463 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
 		"react": "^19.0.0",
 		"react-day-picker": "8.10.1",
 		"react-dom": "^19.0.0",
+		"recharts": "^2.15.3",
 		"server-only": "^0.0.1",
 		"sonner": "^2.0.3",
 		"superjson": "^2.2.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 31c25c5..c5d9a5a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -101,6 +101,9 @@ importers:
       react-dom:
         specifier: ^19.0.0
         version: 19.1.0(react@19.1.0)
+      recharts:
+        specifier: ^2.15.3
+        version: 2.15.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
       server-only:
         specifier: ^0.0.1
         version: 0.0.1
@@ -157,6 +160,10 @@ packages:
     resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
     engines: {node: '>=10'}
 
+  '@babel/runtime@7.27.1':
+    resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==}
+    engines: {node: '>=6.9.0'}
+
   '@better-auth/utils@0.2.4':
     resolution: {integrity: sha512-ayiX87Xd5sCHEplAdeMgwkA0FgnXsEZBgDn890XHHwSWNqqRZDYOq3uj2Ei2leTv1I2KbG5HHn60Ah1i2JWZjQ==}
 
@@ -1334,6 +1341,33 @@ packages:
     peerDependencies:
       typescript: '>=5.7.2'
 
+  '@types/d3-array@3.2.1':
+    resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
+
+  '@types/d3-color@3.1.3':
+    resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+  '@types/d3-ease@3.0.2':
+    resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+  '@types/d3-interpolate@3.0.4':
+    resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+  '@types/d3-path@3.1.1':
+    resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+  '@types/d3-scale@4.0.9':
+    resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+  '@types/d3-shape@3.1.7':
+    resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
+
+  '@types/d3-time@3.0.4':
+    resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+  '@types/d3-timer@3.0.2':
+    resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
   '@types/node@20.17.45':
     resolution: {integrity: sha512-vO9+E1smq+149wsmmLdM8SKVW7gRzLjfo0mU7kiykhV6rL+GEUhUmW7VywJNSxJHQzt9QBIHEo+3SG4MrFTqbA==}
 
@@ -1416,6 +1450,50 @@ packages:
   csstype@3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
+  d3-array@3.2.4:
+    resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+    engines: {node: '>=12'}
+
+  d3-color@3.1.0:
+    resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+    engines: {node: '>=12'}
+
+  d3-ease@3.0.1:
+    resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+    engines: {node: '>=12'}
+
+  d3-format@3.1.0:
+    resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+    engines: {node: '>=12'}
+
+  d3-interpolate@3.0.1:
+    resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+    engines: {node: '>=12'}
+
+  d3-path@3.1.0:
+    resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+    engines: {node: '>=12'}
+
+  d3-scale@4.0.2:
+    resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+    engines: {node: '>=12'}
+
+  d3-shape@3.2.0:
+    resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+    engines: {node: '>=12'}
+
+  d3-time-format@4.1.0:
+    resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+    engines: {node: '>=12'}
+
+  d3-time@3.1.0:
+    resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+    engines: {node: '>=12'}
+
+  d3-timer@3.0.1:
+    resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+    engines: {node: '>=12'}
+
   date-fns@4.1.0:
     resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
 
@@ -1431,6 +1509,9 @@ packages:
       supports-color:
         optional: true
 
+  decimal.js-light@2.5.1:
+    resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
+
   decode-formdata@0.9.0:
     resolution: {integrity: sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==}
 
@@ -1447,6 +1528,9 @@ packages:
   devalue@5.1.1:
     resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
 
+  dom-helpers@5.2.1:
+    resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
+
   drizzle-kit@0.30.6:
     resolution: {integrity: sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g==}
     hasBin: true
@@ -1566,10 +1650,17 @@ packages:
     engines: {node: '>=12'}
     hasBin: true
 
+  eventemitter3@4.0.7:
+    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+
   fast-check@3.23.2:
     resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==}
     engines: {node: '>=8.0.0'}
 
+  fast-equals@5.2.2:
+    resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==}
+    engines: {node: '>=6.0.0'}
+
   file-selector@0.6.0:
     resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==}
     engines: {node: '>= 12'}
@@ -1592,6 +1683,10 @@ packages:
   graceful-fs@4.2.11:
     resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
 
+  internmap@2.0.3:
+    resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+    engines: {node: '>=12'}
+
   is-arrayish@0.3.2:
     resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
 
@@ -1610,6 +1705,9 @@ packages:
   jose@5.10.0:
     resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==}
 
+  js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
   kysely@0.27.6:
     resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==}
     engines: {node: '>=14.0.0'}
@@ -1678,6 +1776,13 @@ packages:
     resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
     engines: {node: '>= 12.0.0'}
 
+  lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+  loose-envify@1.4.0:
+    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+
   lucide-react@0.508.0:
     resolution: {integrity: sha512-gcP16PnexqtOFrTtv98kVsGzTfnbPekzZiQfByi2S89xfk7E/4uKE1USZqccIp58v42LqkO7MuwpCqshwSrJCg==}
     peerDependencies:
@@ -1736,6 +1841,10 @@ packages:
     resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==}
     hasBin: true
 
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
   picocolors@1.1.1:
     resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
 
@@ -1751,6 +1860,9 @@ packages:
     resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
     engines: {node: '>=12'}
 
+  prop-types@15.8.1:
+    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
   pure-rand@6.1.0:
     resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==}
 
@@ -1772,6 +1884,12 @@ packages:
     peerDependencies:
       react: ^19.1.0
 
+  react-is@16.13.1:
+    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+  react-is@18.3.1:
+    resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
   react-remove-scroll-bar@2.3.8:
     resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
     engines: {node: '>=10'}
@@ -1792,6 +1910,12 @@ packages:
       '@types/react':
         optional: true
 
+  react-smooth@4.0.4:
+    resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
   react-style-singleton@2.2.3:
     resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
     engines: {node: '>=10'}
@@ -1802,10 +1926,26 @@ packages:
       '@types/react':
         optional: true
 
+  react-transition-group@4.4.5:
+    resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
+    peerDependencies:
+      react: '>=16.6.0'
+      react-dom: '>=16.6.0'
+
   react@19.1.0:
     resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
     engines: {node: '>=0.10.0'}
 
+  recharts-scale@0.4.5:
+    resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==}
+
+  recharts@2.15.3:
+    resolution: {integrity: sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==}
+    engines: {node: '>=14'}
+    peerDependencies:
+      react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+      react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
   resolve-pkg-maps@1.0.0:
     resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
 
@@ -1888,6 +2028,9 @@ packages:
     resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
     engines: {node: '>=6'}
 
+  tiny-invariant@1.3.3:
+    resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
+
   tslib@2.8.1:
     resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
 
@@ -1951,6 +2094,9 @@ packages:
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
 
+  victory-vendor@36.9.2:
+    resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
+
   which@4.0.0:
     resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
     engines: {node: ^16.13.0 || >=18.0.0}
@@ -1963,6 +2109,8 @@ snapshots:
 
   '@alloc/quick-lru@5.2.0': {}
 
+  '@babel/runtime@7.27.1': {}
+
   '@better-auth/utils@0.2.4':
     dependencies:
       typescript: 5.8.3
@@ -2891,6 +3039,30 @@ snapshots:
     dependencies:
       typescript: 5.8.3
 
+  '@types/d3-array@3.2.1': {}
+
+  '@types/d3-color@3.1.3': {}
+
+  '@types/d3-ease@3.0.2': {}
+
+  '@types/d3-interpolate@3.0.4':
+    dependencies:
+      '@types/d3-color': 3.1.3
+
+  '@types/d3-path@3.1.1': {}
+
+  '@types/d3-scale@4.0.9':
+    dependencies:
+      '@types/d3-time': 3.0.4
+
+  '@types/d3-shape@3.1.7':
+    dependencies:
+      '@types/d3-path': 3.1.1
+
+  '@types/d3-time@3.0.4': {}
+
+  '@types/d3-timer@3.0.2': {}
+
   '@types/node@20.17.45':
     dependencies:
       undici-types: 6.19.8
@@ -2994,6 +3166,44 @@ snapshots:
 
   csstype@3.1.3: {}
 
+  d3-array@3.2.4:
+    dependencies:
+      internmap: 2.0.3
+
+  d3-color@3.1.0: {}
+
+  d3-ease@3.0.1: {}
+
+  d3-format@3.1.0: {}
+
+  d3-interpolate@3.0.1:
+    dependencies:
+      d3-color: 3.1.0
+
+  d3-path@3.1.0: {}
+
+  d3-scale@4.0.2:
+    dependencies:
+      d3-array: 3.2.4
+      d3-format: 3.1.0
+      d3-interpolate: 3.0.1
+      d3-time: 3.1.0
+      d3-time-format: 4.1.0
+
+  d3-shape@3.2.0:
+    dependencies:
+      d3-path: 3.1.0
+
+  d3-time-format@4.1.0:
+    dependencies:
+      d3-time: 3.1.0
+
+  d3-time@3.1.0:
+    dependencies:
+      d3-array: 3.2.4
+
+  d3-timer@3.0.1: {}
+
   date-fns@4.1.0: {}
 
   dayjs@1.11.13: {}
@@ -3002,6 +3212,8 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
+  decimal.js-light@2.5.1: {}
+
   decode-formdata@0.9.0: {}
 
   defu@6.1.4: {}
@@ -3012,6 +3224,11 @@ snapshots:
 
   devalue@5.1.1: {}
 
+  dom-helpers@5.2.1:
+    dependencies:
+      '@babel/runtime': 7.27.1
+      csstype: 3.1.3
+
   drizzle-kit@0.30.6:
     dependencies:
       '@drizzle-team/brocli': 0.10.2
@@ -3098,10 +3315,14 @@ snapshots:
       '@esbuild/win32-ia32': 0.19.12
       '@esbuild/win32-x64': 0.19.12
 
+  eventemitter3@4.0.7: {}
+
   fast-check@3.23.2:
     dependencies:
       pure-rand: 6.1.0
 
+  fast-equals@5.2.2: {}
+
   file-selector@0.6.0:
     dependencies:
       tslib: 2.8.1
@@ -3127,6 +3348,8 @@ snapshots:
 
   graceful-fs@4.2.11: {}
 
+  internmap@2.0.3: {}
+
   is-arrayish@0.3.2:
     optional: true
 
@@ -3138,6 +3361,8 @@ snapshots:
 
   jose@5.10.0: {}
 
+  js-tokens@4.0.0: {}
+
   kysely@0.27.6: {}
 
   lightningcss-darwin-arm64@1.29.2:
@@ -3185,6 +3410,12 @@ snapshots:
       lightningcss-win32-arm64-msvc: 1.29.2
       lightningcss-win32-x64-msvc: 1.29.2
 
+  lodash@4.17.21: {}
+
+  loose-envify@1.4.0:
+    dependencies:
+      js-tokens: 4.0.0
+
   lucide-react@0.508.0(react@19.1.0):
     dependencies:
       react: 19.1.0
@@ -3248,6 +3479,8 @@ snapshots:
       detect-libc: 2.0.4
     optional: true
 
+  object-assign@4.1.1: {}
+
   picocolors@1.1.1: {}
 
   postcss@8.4.31:
@@ -3264,6 +3497,12 @@ snapshots:
 
   postgres@3.4.5: {}
 
+  prop-types@15.8.1:
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+      react-is: 16.13.1
+
   pure-rand@6.1.0: {}
 
   pvtsutils@1.3.6:
@@ -3282,6 +3521,10 @@ snapshots:
       react: 19.1.0
       scheduler: 0.26.0
 
+  react-is@16.13.1: {}
+
+  react-is@18.3.1: {}
+
   react-remove-scroll-bar@2.3.8(@types/react@19.1.3)(react@19.1.0):
     dependencies:
       react: 19.1.0
@@ -3301,6 +3544,14 @@ snapshots:
     optionalDependencies:
       '@types/react': 19.1.3
 
+  react-smooth@4.0.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+    dependencies:
+      fast-equals: 5.2.2
+      prop-types: 15.8.1
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+      react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+
   react-style-singleton@2.2.3(@types/react@19.1.3)(react@19.1.0):
     dependencies:
       get-nonce: 1.0.1
@@ -3309,8 +3560,34 @@ snapshots:
     optionalDependencies:
       '@types/react': 19.1.3
 
+  react-transition-group@4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+    dependencies:
+      '@babel/runtime': 7.27.1
+      dom-helpers: 5.2.1
+      loose-envify: 1.4.0
+      prop-types: 15.8.1
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+
   react@19.1.0: {}
 
+  recharts-scale@0.4.5:
+    dependencies:
+      decimal.js-light: 2.5.1
+
+  recharts@2.15.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+    dependencies:
+      clsx: 2.1.1
+      eventemitter3: 4.0.7
+      lodash: 4.17.21
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+      react-is: 18.3.1
+      react-smooth: 4.0.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      recharts-scale: 0.4.5
+      tiny-invariant: 1.3.3
+      victory-vendor: 36.9.2
+
   resolve-pkg-maps@1.0.0: {}
 
   rou3@0.5.1: {}
@@ -3391,6 +3668,8 @@ snapshots:
 
   tapable@2.2.1: {}
 
+  tiny-invariant@1.3.3: {}
+
   tslib@2.8.1: {}
 
   tw-animate-css@1.2.9: {}
@@ -3431,6 +3710,23 @@ snapshots:
     dependencies:
       react: 19.1.0
 
+  victory-vendor@36.9.2:
+    dependencies:
+      '@types/d3-array': 3.2.1
+      '@types/d3-ease': 3.0.2
+      '@types/d3-interpolate': 3.0.4
+      '@types/d3-scale': 4.0.9
+      '@types/d3-shape': 3.1.7
+      '@types/d3-time': 3.0.4
+      '@types/d3-timer': 3.0.2
+      d3-array: 3.2.4
+      d3-ease: 3.0.1
+      d3-interpolate: 3.0.1
+      d3-scale: 4.0.2
+      d3-shape: 3.2.0
+      d3-time: 3.1.0
+      d3-timer: 3.0.1
+
   which@4.0.0:
     dependencies:
       isexe: 3.1.1
diff --git a/src/app/(home)/layout.tsx b/src/app/(home)/layout.tsx
index 289f28c..de96a44 100644
--- a/src/app/(home)/layout.tsx
+++ b/src/app/(home)/layout.tsx
@@ -9,7 +9,7 @@ import {
   SidebarTrigger,
 } from "~/components/ui/sidebar"
 
-const Layout =  ({ children }: { children: React.ReactNode }) => {
+const Layout = ({ children }: { children: React.ReactNode }) => {
   return (
     <div>
       <SidebarProvider>
diff --git a/src/app/(home)/logs/glucose/page.tsx b/src/app/(home)/logs/glucose/page.tsx
index 16e9dcb..a3d5f3a 100644
--- a/src/app/(home)/logs/glucose/page.tsx
+++ b/src/app/(home)/logs/glucose/page.tsx
@@ -1,27 +1,39 @@
 import GlucoseForm from '~/components/logs/forms/glucose'
+import GlucoseHistory from '~/components/logs/history/glucose'
 import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs'
-const GlucoseLogsPage = () => {
+import { api, HydrateClient } from '~/trpc/server'
+
+type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>
+
+const GlucoseLogsPage = async (props : {
+    searchParams: SearchParams
+}) => {
+    void api.glucose.getLogs.prefetch()
+
+    const searchParams = await props.searchParams
+    const timePeriod = searchParams.timePeriod
+
     return (
-        <div>
-            <h2 className='page-title'>
-                Glucose Logs
-            </h2>
-            <Tabs defaultValue='upload'>
-                <TabsList>
-                    <TabsTrigger value='history'>History</TabsTrigger>
-                    <TabsTrigger value='upload'>Upload</TabsTrigger>
-                </TabsList>
-                <TabsContent value='history'>
-                    <div>
-                        <h3>History</h3>
-                    </div>
-                </TabsContent>
-                <TabsContent value='upload'>
-                    <GlucoseForm />
-                </TabsContent>
-                
-            </Tabs>
-        </div>
+        <HydrateClient>
+            <div>
+                <h2 className='page-title'>
+                    Glucose Logs
+                </h2>
+                <Tabs defaultValue='history'>
+                    <TabsList>
+                        <TabsTrigger value='history'>History</TabsTrigger>
+                        <TabsTrigger value='upload'>Upload</TabsTrigger>
+                    </TabsList>
+                    <TabsContent value='history'>
+                        <GlucoseHistory timePeriod={timePeriod as string ?? ''} />
+                    </TabsContent>
+                    <TabsContent value='upload'>
+                        <GlucoseForm />
+                    </TabsContent>
+
+                </Tabs>
+            </div>
+        </HydrateClient>
     )
 }
 
diff --git a/src/components/auth/forms/sign-in.tsx b/src/components/auth/forms/sign-in.tsx
index 7be6516..457c143 100644
--- a/src/components/auth/forms/sign-in.tsx
+++ b/src/components/auth/forms/sign-in.tsx
@@ -75,7 +75,6 @@ const SignInForm = () => {
                     )}
                 />
             </div>
-
             <div className="grid gap-2">
                 <div className="flex items-center">
                     <Label htmlFor="password">Password</Label>
@@ -104,7 +103,6 @@ const SignInForm = () => {
                     )}
                 />
             </div>
-
             <div className="flex items-center gap-2">
                 <form.Field
                     name="rememberMe"
diff --git a/src/components/auth/forms/sign-up.tsx b/src/components/auth/forms/sign-up.tsx
index 22c0ed6..7109b59 100644
--- a/src/components/auth/forms/sign-up.tsx
+++ b/src/components/auth/forms/sign-up.tsx
@@ -8,8 +8,9 @@ import { Loader2 } from "lucide-react";
 import { useRouter } from "next/navigation";
 import { toast } from "sonner";
 import { Label } from "~/components/ui/label";
-import { signUp, useSession } from "~/lib/auth-client";
+import { signUp } from "~/lib/auth-client";
 import { signUpSchema } from "~/schemas/auth";
+import { Checkbox } from "~/components/ui/checkbox";
 
 const SignUpForm = () => {
     const router = useRouter()
@@ -21,6 +22,7 @@ const SignUpForm = () => {
             email: "",
             password: "",
             confirmPassword: "",
+            rememberMe: false
         }
     })
 
@@ -48,6 +50,8 @@ const SignUpForm = () => {
         },
     })
 
+    console.log(form.state.errors)
+
     return (
         <form onSubmit={(e) => {
             e.preventDefault()
@@ -172,6 +176,21 @@ const SignUpForm = () => {
                     )}
                 />
             </div>
+            <div className="flex items-center gap-2">
+                <form.Field
+                    name="rememberMe"
+                    children={(field) => (
+                        <>
+                            <Checkbox
+                                id="remember"
+                                checked={field.state.value}
+                                onCheckedChange={(checked) => field.handleChange(checked === true)}
+                            />
+                            <Label htmlFor="remember">Remember me</Label>
+                        </>
+                    )}
+                />
+            </div>
             <form.Subscribe
                 selector={(state) => [state.canSubmit, state.isSubmitting]}
                 children={([canSubmit, isSubmitting]) => (
diff --git a/src/components/charts/glucose/glucose-distribution.tsx b/src/components/charts/glucose/glucose-distribution.tsx
new file mode 100644
index 0000000..281284e
--- /dev/null
+++ b/src/components/charts/glucose/glucose-distribution.tsx
@@ -0,0 +1,66 @@
+'use client'
+
+import { ChartTooltipContent, ChartTooltip, type ChartConfig, } from "~/components/ui/chart"
+import { CartesianGrid, Scatter, ScatterChart, XAxis, YAxis } from "recharts"
+import { ChartContainer } from "~/components/ui/chart"
+import { CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card"
+import { Card } from "~/components/ui/card"
+import type { GetGlucoseLogsOutput } from "~/trpc/react"
+import dayjs from "dayjs"
+
+export const GlucoseDistributionChart = ({ glucoseLogs, chartConfig }: { glucoseLogs: GetGlucoseLogsOutput, chartConfig: ChartConfig }) => {
+    return (
+        <div>
+            <Card className="mt-2">
+                <CardHeader>
+                    <CardTitle>Glucose distribution</CardTitle>
+                    <CardDescription>
+                        {glucoseLogs.length > 0 ? `${dayjs(glucoseLogs?.[0]?.date).format("MMMM YYYY")} to ${dayjs(glucoseLogs?.[glucoseLogs.length - 1]?.date).format("MMMM YYYY")}` : "No glucose logs found"}
+                    </CardDescription>
+                </CardHeader>
+                <CardContent>
+                    {glucoseLogs.length > 0 ? (
+                        <ChartContainer config={chartConfig}>
+                            <ScatterChart
+                                accessibilityLayer
+                                data={glucoseLogs}
+                                margin={{
+                                    left: 12,
+                                    right: 12,
+                                }}
+                            >
+                                <CartesianGrid vertical={false} />
+                                <XAxis
+                                    dataKey="date"
+                                    tickLine={false}
+                                    axisLine={false}
+                                    tickMargin={8}
+                                    tickFormatter={(value) => dayjs(value).format("DD MMM")}
+                                />
+                                <YAxis
+                                    dataKey="glucose"
+                                    tickLine={false}
+                                    axisLine={false}
+                                    tickMargin={8}
+                                />
+                                <ChartTooltip
+                                    cursor={false}
+                                    content={<ChartTooltipContent hideLabel />}
+                                />
+                                <Scatter
+                                    dataKey="glucose"
+                                    type="natural"
+                                    fill="var(--chart-5)"
+                                />
+                            </ScatterChart>
+                        </ChartContainer>
+                    ) : (
+                        <div className="flex items-center justify-center h-full">
+                            <p className="text-sm text-muted-foreground">No glucose logs found</p>
+                        </div>
+                    )}
+                </CardContent>
+            </Card>
+        </div>
+    )
+}
\ No newline at end of file
diff --git a/src/components/charts/glucose/glucose-history.tsx b/src/components/charts/glucose/glucose-history.tsx
new file mode 100644
index 0000000..9d9834e
--- /dev/null
+++ b/src/components/charts/glucose/glucose-history.tsx
@@ -0,0 +1,65 @@
+'use client'
+
+import dayjs from "dayjs"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card"
+import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "~/components/ui/chart"
+import type { GetGlucoseLogsOutput } from "~/trpc/react"
+
+export const GlucoseHistoryChart = ({ glucoseLogs, chartConfig }: { glucoseLogs: GetGlucoseLogsOutput, chartConfig: ChartConfig }) => {
+    return (
+        <div>
+            <Card className="mt-2">
+                <CardHeader>
+                    <CardTitle className="flex items-center justify-between">
+                        <span>
+                            Glucose levels
+                        </span>
+                    </CardTitle>
+                    <CardDescription>
+                        {glucoseLogs.length > 0 ? `${dayjs(glucoseLogs?.[0]?.date).format("MMMM YYYY")} to ${dayjs(glucoseLogs?.[glucoseLogs.length - 1]?.date).format("MMMM YYYY")}` : "No glucose logs found"}
+                    </CardDescription>
+                </CardHeader>
+                <CardContent>
+                    {glucoseLogs.length > 0 ? (
+                        <ChartContainer config={chartConfig}>
+                            <LineChart
+                                accessibilityLayer
+                                data={glucoseLogs}
+                                margin={{
+                                    left: 12,
+                                    right: 12,
+                                }}
+                            >
+                                <CartesianGrid vertical={false} />
+                                <XAxis
+                                    dataKey="date"
+                                    tickLine={false}
+                                    axisLine={false}
+                                    tickMargin={8}
+                                    tickFormatter={(value) => dayjs(value).format("DD MMM")}
+                                />
+                                <ChartTooltip
+                                    cursor={false}
+                                    content={<ChartTooltipContent hideLabel />}
+                                />
+                                <Line
+                                    dataKey="glucose"
+                                    type="natural"
+                                    stroke="var(--chart-5)"
+                                    strokeWidth={2}
+                                    dot={false}
+                                />
+                            </LineChart>
+                        </ChartContainer>
+                    ) : (
+                        <div className="flex items-center justify-center h-full">
+                            <p className="text-sm text-muted-foreground">No glucose logs found</p>
+                        </div>
+                    )}
+                </CardContent>
+            </Card >
+        </div>
+    )
+}
+
diff --git a/src/components/charts/glucose/glucose-pie.tsx b/src/components/charts/glucose/glucose-pie.tsx
new file mode 100644
index 0000000..cb4fd9f
--- /dev/null
+++ b/src/components/charts/glucose/glucose-pie.tsx
@@ -0,0 +1,90 @@
+'use client'
+
+import type { GetGlucoseLogsOutput } from '~/trpc/react'
+
+import { Pie, PieChart } from "recharts"
+
+import {
+    Card,
+    CardContent,
+    CardDescription,
+    CardHeader,
+    CardTitle
+} from "~/components/ui/card"
+
+import {
+    ChartContainer,
+    ChartTooltip,
+    ChartTooltipContent,
+    type ChartConfig
+} from "~/components/ui/chart"
+import dayjs from 'dayjs'
+
+type Props = {
+    glucoseLogs: GetGlucoseLogsOutput
+}
+
+const chartConfig = {
+    value: {
+        label: "Value"
+    },
+    high: {
+        color: "hsl(var(--chart-1))",
+        label: "High"
+    },
+    low: {
+        color: "hsl(var(--chart-2))",
+        label: "Low"
+    }
+} satisfies ChartConfig
+
+const GlucosePieChart = ({ glucoseLogs }: Props) => {
+    const highVLows = glucoseLogs.reduce((acc, log) => {
+        if (log.glucose > 144) {
+            acc.high += 1
+        } else if (log.glucose < 72) {
+            acc.low += 1
+        }
+        return acc
+    }, { high: 0, low: 0 })
+
+    const chartData = [
+        { name: "high", value: highVLows.high },
+        { name: "low", value: highVLows.low },
+    ]
+
+    return (
+        <Card className="flex flex-col">
+            <CardHeader className="items-center pb-0">
+                <CardTitle>
+                    Glucose high & low distribution
+                </CardTitle>
+                <CardDescription>
+                    {glucoseLogs.length > 0 ? `${dayjs(glucoseLogs?.[0]?.date).format("MMMM YYYY")} to ${dayjs(glucoseLogs?.[glucoseLogs.length - 1]?.date).format("MMMM YYYY")}` : "No glucose logs found"}
+                </CardDescription>
+            </CardHeader>
+            <CardContent className="flex-1 pb-0">
+                {glucoseLogs.length > 0 ? (
+                    <ChartContainer
+                        config={chartConfig}
+                        className="mx-auto aspect-square max-h-[250px]"
+                    >
+                    <PieChart>
+                        <ChartTooltip
+                            cursor={false}
+                            content={<ChartTooltipContent hideLabel />}
+                        />
+                        <Pie data={chartData} dataKey="value" nameKey="name" />
+                    </PieChart>
+                    </ChartContainer>
+                ) : (
+                    <div className="flex items-center justify-center h-full">
+                        <p className="text-sm text-muted-foreground">No glucose logs found</p>
+                    </div>
+                )}
+            </CardContent>
+        </Card>
+    )
+}
+
+export default GlucosePieChart
\ No newline at end of file
diff --git a/src/components/logs/common/filter-labels.tsx b/src/components/logs/common/filter-labels.tsx
new file mode 100644
index 0000000..89eb0f2
--- /dev/null
+++ b/src/components/logs/common/filter-labels.tsx
@@ -0,0 +1,25 @@
+import { cn, removeSearchParam } from "~/lib/utils"
+
+import { Badge } from "~/components/ui/badge"
+import { XIcon } from "lucide-react"
+import { buttonVariants } from "~/components/ui/button"
+import Link from "next/link"
+
+const FilterLabels = ({ timePeriodLabel, pathname }: { timePeriodLabel: string, pathname: string }) => {
+    return (
+        <div className="flex items-center gap-2 flex-wrap">
+            <Badge asChild variant="outline">
+                <div className="flex items-center gap-2">
+                    <span>{timePeriodLabel}</span>
+                    <Link href={removeSearchParam(pathname, "timePeriod")} className={cn(buttonVariants({
+                        variant: "outline",
+                    }), "size-1")}>
+                        <XIcon className="size-3" />
+                    </Link>
+                </div>
+            </Badge>
+        </div>
+    )
+}
+
+export default FilterLabels
\ No newline at end of file
diff --git a/src/components/logs/common/filter-logs.tsx b/src/components/logs/common/filter-logs.tsx
new file mode 100644
index 0000000..4777f9c
--- /dev/null
+++ b/src/components/logs/common/filter-logs.tsx
@@ -0,0 +1,67 @@
+import { FilterIcon } from "lucide-react"
+import Link from "next/link"
+
+import { Button, buttonVariants } from "~/components/ui/button"
+
+import { Sheet, SheetDescription, SheetTitle, SheetHeader, SheetContent, SheetTrigger } from "~/components/ui/sheet"
+import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip"
+import { cn } from "~/lib/utils"
+
+type Props = {
+    tooltipContent: string
+}
+
+const FilterLogs = ({ tooltipContent }: Props) => {
+    return (
+        <Sheet  >
+            <Tooltip>
+                <SheetTrigger asChild>
+                    <TooltipTrigger asChild>
+                        <Button variant="outline" size="icon">
+                            <FilterIcon className="size-4" />
+                        </Button>
+                    </TooltipTrigger>
+                </SheetTrigger>
+                <TooltipContent>
+                    <p>{tooltipContent}</p>
+                </TooltipContent>
+            </Tooltip>
+            <SheetContent>
+                <SheetHeader>
+                    <SheetTitle>Filter glucose levels</SheetTitle>
+                    <SheetDescription>
+                        <div>
+                            <p className="text-sm font-medium">
+                                Time period
+                            </p>
+                            <div className="grid grid-cols-3 gap-2 mt-2">
+                                <Link href={`/logs/glucose`} className={cn(buttonVariants({
+                                    variant: "outline",
+                                    size: 'sm'
+                                }), "size-full")}>
+                                    <p>
+                                        All
+                                    </p>
+                                </Link>
+                                <Link href={`/logs/glucose?timePeriod=lastWeek`} className={cn(buttonVariants({
+                                    variant: "outline",
+                                    size: 'sm'
+                                }), "size-full")}>
+                                    <p>Last week</p>
+                                </Link>
+                                <Link href={`/logs/glucose?timePeriod=lastMonth`} className={cn(buttonVariants({
+                                    variant: "outline",
+                                    size: 'sm'
+                                }), "size-full")}>
+                                    <p>Last month</p>
+                                </Link>
+                            </div>
+                        </div>
+                    </SheetDescription>
+                </SheetHeader>
+            </SheetContent>
+        </Sheet>
+    )
+}
+
+export default FilterLogs
\ No newline at end of file
diff --git a/src/components/logs/forms/glucose.tsx b/src/components/logs/forms/glucose.tsx
index 585e038..335b5c8 100644
--- a/src/components/logs/forms/glucose.tsx
+++ b/src/components/logs/forms/glucose.tsx
@@ -163,7 +163,7 @@ const GlucoseForm = () => {
                                                 <Button
                                                     variant="outline"
                                                     className={cn(
-                                                        "w-full md:w-[280px] justify-start text-left font-normal",
+                                                        "w-full justify-start text-left font-normal",
                                                         !field.state.value && "text-muted-foreground"
                                                     )}
                                                 >
@@ -185,7 +185,11 @@ const GlucoseForm = () => {
                                                 />
                                                 <div className="p-3 border-t border-border">
                                                     <TimePicker
-                                                        setDate={(date) => field.handleChange(toISOStringWithTimezone(date ?? new Date()))}
+                                                        setDate={(date) => {
+                                                            if(!date) return;
+
+                                                            field.handleChange(toISOStringWithTimezone(date))
+                                                        }}
                                                         date={new Date(field.state.value)}
                                                     />
                                                 </div>
@@ -230,7 +234,7 @@ const GlucoseForm = () => {
                         children={([canSubmit, isSubmitting]) => (
                             <div className='flex gap-2'>
                                 <Button type='submit' disabled={!canSubmit || isSubmitting || isGlucoseLogPending}>
-                                    {isSubmitting || isGlucoseLogPending ? <Loader2 className='size-4 animate-spin' /> : 'Submit'}
+                                    {isSubmitting || isGlucoseLogPending ? <Loader2 className='size-4 animate-spin' /> : 'Upload log'}
                                 </Button>
                                 <Button type='button' variant={'outline'} onClick={() => {
                                     form.reset()
diff --git a/src/components/logs/history/glucose.tsx b/src/components/logs/history/glucose.tsx
new file mode 100644
index 0000000..2d8b2ae
--- /dev/null
+++ b/src/components/logs/history/glucose.tsx
@@ -0,0 +1,123 @@
+"use client"
+
+import { api, type GetGlucoseLogsOutput } from '~/trpc/react'
+
+import dayjs from "dayjs"
+import { usePathname } from "next/navigation"
+import { useEffect, useState } from "react"
+import { GlucoseDistributionChart } from "~/components/charts/glucose/glucose-distribution"
+import { GlucoseHistoryChart } from "~/components/charts/glucose/glucose-history"
+import {
+    type ChartConfig
+} from "~/components/ui/chart"
+import { cn } from "~/lib/utils"
+import FilterLabels from "../common/filter-labels"
+import FilterGlucoseLogs from "../common/filter-logs"
+import GlucosePieChart from '~/components/charts/glucose/glucose-pie'
+import { Skeleton } from "~/components/ui/skeleton"
+import { toast } from 'sonner'
+
+const chartConfig = {
+    glucose: {
+        label: "Glucose",
+        color: "hsl(var(--chart-5))",
+    }
+} satisfies ChartConfig
+
+const GlucoseHistorySkeleton = () => {
+    return (
+        <div className="flex flex-col">
+            <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <Skeleton className="h-8 w-32" />
+                <Skeleton className="h-8 w-8" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+            </div>
+        </div>
+    )
+}
+
+const GlucoseHistory = ({ timePeriod }: { timePeriod: string }) => {
+    const { data, isLoading: isGlucoseLogsLoading, error } = api.glucose.getLogs.useQuery()
+
+    const [glucoseLogs, setGlucoseLogs] = useState<GetGlucoseLogsOutput>([])
+    const pathname = usePathname()
+
+    useEffect(() => {
+        if (!data) return
+
+        if (timePeriod === "lastWeek") {
+            setGlucoseLogs(data.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
+        } else if (timePeriod === "lastMonth") {
+            setGlucoseLogs(data.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
+        } else {
+            setGlucoseLogs(data)
+        }
+    }, [timePeriod, data])
+
+    if (error) {
+        toast.error("Error fetching glucose logs")
+        console.error(error)
+    }
+
+    const timePeriodLabel = timePeriod === "lastWeek" ? "Last week" : timePeriod === "lastMonth" ? "Last month" : null
+
+    if (isGlucoseLogsLoading) {
+        return <GlucoseHistorySkeleton />
+    }
+
+    if (glucoseLogs.length === 0) {
+        return (
+            <div className="flex flex-col">
+                <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                    <p className="text-sm text-muted-foreground">
+                        Start tracking your glucose levels to see them here.
+                    </p>
+                </div>
+            </div>
+        )
+    }
+
+    return (
+        <div className="flex flex-col">
+            <div className={cn("flex flex-col sm:flex-row items-start sm:items-center gap-2", {
+                "justify-end": !!!timePeriodLabel,
+                "justify-between": !!timePeriodLabel,
+            })}>
+                {timePeriodLabel && <FilterLabels timePeriodLabel={timePeriodLabel} pathname={pathname} />}
+                <FilterGlucoseLogs tooltipContent="Filter glucose levels" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-1">
+                    <GlucoseHistoryChart glucoseLogs={glucoseLogs ?? []} chartConfig={chartConfig} />
+                </div>
+                <div className="lg:col-span-1">
+                    <GlucoseDistributionChart glucoseLogs={glucoseLogs ?? []} chartConfig={chartConfig} />
+                </div>
+                <div className="lg:col-span-1">
+                    <GlucosePieChart glucoseLogs={glucoseLogs ?? []} />
+                </div>
+            </div>
+        </div>
+    )
+}
+
+export default GlucoseHistory
\ No newline at end of file
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
new file mode 100644
index 0000000..fc40406
--- /dev/null
+++ b/src/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "~/lib/utils"
+
+const badgeVariants = cva(
+  "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
+  {
+    variants: {
+      variant: {
+        default:
+          "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+        secondary:
+          "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+        destructive:
+          "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+        outline:
+          "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+      },
+    },
+    defaultVariants: {
+      variant: "default",
+    },
+  }
+)
+
+function Badge({
+  className,
+  variant,
+  asChild = false,
+  ...props
+}: React.ComponentProps<"span"> &
+  VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
+  const Comp = asChild ? Slot : "span"
+
+  return (
+    <Comp
+      data-slot="badge"
+      className={cn(badgeVariants({ variant }), className)}
+      {...props}
+    />
+  )
+}
+
+export { Badge, badgeVariants }
diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx
new file mode 100644
index 0000000..54ec951
--- /dev/null
+++ b/src/components/ui/chart.tsx
@@ -0,0 +1,350 @@
+"use client"
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "~/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+  [k in string]: {
+    label?: React.ReactNode
+    icon?: React.ComponentType
+  } & (
+    | { color?: string; theme?: never }
+    | { color?: never; theme: Record<keyof typeof THEMES, string> }
+  )
+}
+
+type ChartContextProps = {
+  config: ChartConfig
+}
+
+const ChartContext = React.createContext<ChartContextProps | null>(null)
+
+function useChart() {
+  const context = React.useContext(ChartContext)
+
+  if (!context) {
+    throw new Error("useChart must be used within a <ChartContainer />")
+  }
+
+  return context
+}
+
+function ChartContainer({
+  id,
+  className,
+  children,
+  config,
+  ...props
+}: React.ComponentProps<"div"> & {
+  config: ChartConfig
+  children: React.ComponentProps<
+    typeof RechartsPrimitive.ResponsiveContainer
+  >["children"]
+}) {
+  const uniqueId = React.useId()
+  const safeId = (id ?? uniqueId).replace(/[^\w-]/g, "")
+  const chartId = `chart-${safeId}`
+
+  return (
+    <ChartContext.Provider value={{ config }}>
+      <div
+        data-slot="chart"
+        data-chart={chartId}
+        className={cn(
+          "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
+          className
+        )}
+        {...props}
+      >
+        <ChartStyle id={chartId} config={config} />
+        <RechartsPrimitive.ResponsiveContainer>
+          {children}
+        </RechartsPrimitive.ResponsiveContainer>
+      </div>
+    </ChartContext.Provider>
+  )
+}
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+  const colorConfig = Object.entries(config).filter(
+    ([, config]) => config.theme || config.color
+  )
+
+  if (!colorConfig.length) {
+    return null
+  }
+
+  const css = Object.entries(THEMES)
+    .map(([theme, prefix]) => {
+      const vars = colorConfig
+        .map(([key, itemConfig]) => {
+          const color =
+            itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
+            itemConfig.color
+          return color ? `  --color-${key}: ${color};` : null
+        })
+        .filter(Boolean)
+        .join("\n")
+      return `${prefix} [data-chart=${id}] {\n${vars}\n}`
+    })
+    .join("\n")
+
+  return (
+    <style>{css}</style>
+  )
+}
+
+const ChartTooltip = RechartsPrimitive.Tooltip
+
+function ChartTooltipContent({
+  active,
+  payload,
+  className,
+  indicator = "dot",
+  hideLabel = false,
+  hideIndicator = false,
+  label,
+  labelFormatter,
+  labelClassName,
+  formatter,
+  color,
+  nameKey,
+  labelKey,
+}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
+  React.ComponentProps<"div"> & {
+    hideLabel?: boolean
+    hideIndicator?: boolean
+    indicator?: "line" | "dot" | "dashed"
+    nameKey?: string
+    labelKey?: string
+  }) {
+  const { config } = useChart()
+
+  const tooltipLabel = React.useMemo(() => {
+    if (hideLabel || !payload?.length) {
+      return null
+    }
+
+    const [item] = payload
+    const key = `${labelKey || item?.dataKey || item?.name || "value"}`
+    const itemConfig = getPayloadConfigFromPayload(config, item, key)
+    const value =
+      !labelKey && typeof label === "string"
+        ? config[label as keyof typeof config]?.label || label
+        : itemConfig?.label
+
+    if (labelFormatter) {
+      return (
+        <div className={cn("font-medium", labelClassName)}>
+          {labelFormatter(value, payload)}
+        </div>
+      )
+    }
+
+    if (!value) {
+      return null
+    }
+
+    return <div className={cn("font-medium", labelClassName)}>{value}</div>
+  }, [
+    label,
+    labelFormatter,
+    payload,
+    hideLabel,
+    labelClassName,
+    config,
+    labelKey,
+  ])
+
+  if (!active || !payload?.length) {
+    return null
+  }
+
+  const nestLabel = payload.length === 1 && indicator !== "dot"
+
+  return (
+    <div
+      className={cn(
+        "border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
+        className
+      )}
+    >
+      {!nestLabel ? tooltipLabel : null}
+      <div className="grid gap-1.5">
+        {payload.map((item, index) => {
+          const key = `${nameKey || item.name || item.dataKey || "value"}`
+          const itemConfig = getPayloadConfigFromPayload(config, item, key)
+          const indicatorColor = color || item.payload.fill || item.color
+
+          return (
+            <div
+              key={item.dataKey}
+              className={cn(
+                "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
+                indicator === "dot" && "items-center"
+              )}
+            >
+              {formatter && item?.value !== undefined && item.name ? (
+                formatter(item.value, item.name, item, index, item.payload)
+              ) : (
+                <>
+                  {itemConfig?.icon ? (
+                    <itemConfig.icon />
+                  ) : (
+                    !hideIndicator && (
+                      <div
+                        className={cn(
+                          "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
+                          {
+                            "h-2.5 w-2.5": indicator === "dot",
+                            "w-1": indicator === "line",
+                            "w-0 border-[1.5px] border-dashed bg-transparent":
+                              indicator === "dashed",
+                            "my-0.5": nestLabel && indicator === "dashed",
+                          }
+                        )}
+                        style={
+                          {
+                            "--color-bg": indicatorColor,
+                            "--color-border": indicatorColor,
+                          } as React.CSSProperties
+                        }
+                      />
+                    )
+                  )}
+                  <div
+                    className={cn(
+                      "flex flex-1 justify-between leading-none",
+                      nestLabel ? "items-end" : "items-center"
+                    )}
+                  >
+                    <div className="grid gap-1.5">
+                      {nestLabel ? tooltipLabel : null}
+                      <span className="text-muted-foreground">
+                        {itemConfig?.label || item.name}
+                      </span>
+                    </div>
+                    {item.value !== undefined && item.value !== null && (
+                      <span className="text-foreground font-mono font-medium tabular-nums">
+                        {item.value.toLocaleString()}
+                      </span>
+                    )}
+                  </div>
+                </>
+              )}
+            </div>
+          )
+        })}
+      </div>
+    </div>
+  )
+}
+
+const ChartLegend = RechartsPrimitive.Legend
+
+function ChartLegendContent({
+  className,
+  hideIcon = false,
+  payload,
+  verticalAlign = "bottom",
+  nameKey,
+}: React.ComponentProps<"div"> &
+  Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
+    hideIcon?: boolean
+    nameKey?: string
+  }) {
+  const { config } = useChart()
+
+  if (!payload?.length) {
+    return null
+  }
+
+  return (
+    <div
+      className={cn(
+        "flex items-center justify-center gap-4",
+        verticalAlign === "top" ? "pb-3" : "pt-3",
+        className
+      )}
+    >
+      {payload.map((item) => {
+        const key = `${nameKey || item.dataKey || "value"}`
+        const itemConfig = getPayloadConfigFromPayload(config, item, key)
+
+        return (
+          <div
+            key={item.value}
+            className={cn(
+              "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"
+            )}
+          >
+            {itemConfig?.icon && !hideIcon ? (
+              <itemConfig.icon />
+            ) : (
+              <div
+                className="h-2 w-2 shrink-0 rounded-[2px]"
+                style={{
+                  backgroundColor: item.color,
+                }}
+              />
+            )}
+            {itemConfig?.label}
+          </div>
+        )
+      })}
+    </div>
+  )
+}
+
+// Helper to extract item config from a payload.
+function getPayloadConfigFromPayload(
+  config: ChartConfig,
+  payload: unknown,
+  key: string
+) {
+  if (typeof payload !== "object" || payload === null) {
+    return undefined
+  }
+
+  const payloadPayload =
+    "payload" in payload &&
+      typeof payload.payload === "object" &&
+      payload.payload !== null
+      ? payload.payload
+      : undefined
+
+  let configLabelKey: string = key
+
+  if (
+    key in payload &&
+    typeof payload[key as keyof typeof payload] === "string"
+  ) {
+    configLabelKey = payload[key as keyof typeof payload] as string
+  } else if (
+    payloadPayload &&
+    key in payloadPayload &&
+    typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
+  ) {
+    configLabelKey = payloadPayload[
+      key as keyof typeof payloadPayload
+    ] as string
+  }
+
+  return configLabelKey in config
+    ? config[configLabelKey]
+    : config[key as keyof typeof config]
+}
+
+export {
+  ChartContainer,
+  ChartTooltip,
+  ChartTooltipContent,
+  ChartLegend,
+  ChartLegendContent,
+  ChartStyle,
+}
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..84ab5d7
--- /dev/null
+++ b/src/components/ui/dialog.tsx
@@ -0,0 +1,135 @@
+"use client"
+
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { XIcon } from "lucide-react"
+
+import { cn } from "~/lib/utils"
+
+function Dialog({
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Root>) {
+  return <DialogPrimitive.Root data-slot="dialog" {...props} />
+}
+
+function DialogTrigger({
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
+  return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
+}
+
+function DialogPortal({
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
+  return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
+}
+
+function DialogClose({
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Close>) {
+  return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
+}
+
+function DialogOverlay({
+  className,
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
+  return (
+    <DialogPrimitive.Overlay
+      data-slot="dialog-overlay"
+      className={cn(
+        "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
+        className
+      )}
+      {...props}
+    />
+  )
+}
+
+function DialogContent({
+  className,
+  children,
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Content>) {
+  return (
+    <DialogPortal data-slot="dialog-portal">
+      <DialogOverlay />
+      <DialogPrimitive.Content
+        data-slot="dialog-content"
+        className={cn(
+          "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
+          className
+        )}
+        {...props}
+      >
+        {children}
+        <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
+          <XIcon />
+          <span className="sr-only">Close</span>
+        </DialogPrimitive.Close>
+      </DialogPrimitive.Content>
+    </DialogPortal>
+  )
+}
+
+function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
+  return (
+    <div
+      data-slot="dialog-header"
+      className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
+      {...props}
+    />
+  )
+}
+
+function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
+  return (
+    <div
+      data-slot="dialog-footer"
+      className={cn(
+        "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
+        className
+      )}
+      {...props}
+    />
+  )
+}
+
+function DialogTitle({
+  className,
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Title>) {
+  return (
+    <DialogPrimitive.Title
+      data-slot="dialog-title"
+      className={cn("text-lg leading-none font-semibold", className)}
+      {...props}
+    />
+  )
+}
+
+function DialogDescription({
+  className,
+  ...props
+}: React.ComponentProps<typeof DialogPrimitive.Description>) {
+  return (
+    <DialogPrimitive.Description
+      data-slot="dialog-description"
+      className={cn("text-muted-foreground text-sm", className)}
+      {...props}
+    />
+  )
+}
+
+export {
+  Dialog,
+  DialogClose,
+  DialogContent,
+  DialogDescription,
+  DialogFooter,
+  DialogHeader,
+  DialogOverlay,
+  DialogPortal,
+  DialogTitle,
+  DialogTrigger,
+}
diff --git a/src/env.js b/src/env.js
index 5d44414..f151acd 100644
--- a/src/env.js
+++ b/src/env.js
@@ -27,6 +27,7 @@ export const env = createEnv({
 	client: {
 		// NEXT_PUBLIC_CLIENTVAR: z.string(),
 		NEXT_PUBLIC_BETTER_AUTH_URL: z.string().url(),
+		NEXT_PUBLIC_APP_URL: z.string().url(),
 	},
 
 	/**
@@ -35,6 +36,7 @@ export const env = createEnv({
 	 */
 	runtimeEnv: {
 		DATABASE_URL: process.env.DATABASE_URL,
+		NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
 		NODE_ENV: process.env.NODE_ENV,
 		BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET,
 		NEXT_PUBLIC_BETTER_AUTH_URL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL,
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 584c802..b9954c5 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -18,3 +18,8 @@ export function capitalize(str: string) {
   return str.charAt(0).toUpperCase() + str.slice(1)
 }
 
+export function removeSearchParam(pathname: string, param: string) {
+  const urlObj = new URL(`${process.env.NEXT_PUBLIC_APP_URL}${pathname}`)
+  urlObj.searchParams.delete(param)
+  return urlObj.pathname + urlObj.search
+}
diff --git a/src/server/api/routers/glucose.ts b/src/server/api/routers/glucose.ts
index e911735..52d8191 100644
--- a/src/server/api/routers/glucose.ts
+++ b/src/server/api/routers/glucose.ts
@@ -1,7 +1,8 @@
+import { TRPCError } from "@trpc/server";
+import { eq } from "drizzle-orm";
 import { glucoseFormSchema } from "~/schemas/logs";
 import { glucoseLog } from "~/server/db/schema";
 import { createTRPCRouter, protectedProcedure } from "../trpc";
-import { TRPCError } from "@trpc/server";
 
 export const glucoseRouter = createTRPCRouter({
     create: protectedProcedure.input(glucoseFormSchema).mutation(async ({ ctx: { db, auth }, input }) => {
@@ -32,4 +33,27 @@ export const glucoseRouter = createTRPCRouter({
             }
         }
     }),
+    getLogs: protectedProcedure.query(async ({ ctx: { db, auth } }) => {
+        try {
+            return await db.select({
+                id: glucoseLog.id,
+                glucose: glucoseLog.glucose,
+                date: glucoseLog.date,
+            }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date).limit(100)
+        } catch (error) {
+            if (error instanceof TRPCError) throw error;
+
+            if (error instanceof Error) {
+                throw new TRPCError({
+                    code: "INTERNAL_SERVER_ERROR",
+                    message: error.message
+                })
+            } else {
+                throw new TRPCError({
+                    code: "INTERNAL_SERVER_ERROR",
+                    message: "An unknown error occurred"
+                })
+            }
+        }
+    })
 });
diff --git a/src/trpc/react.tsx b/src/trpc/react.tsx
index 395ca86..dd995d7 100644
--- a/src/trpc/react.tsx
+++ b/src/trpc/react.tsx
@@ -33,10 +33,11 @@ export type RouterInputs = inferRouterInputs<AppRouter>;
 
 /**
  * Inference helper for outputs.
- *
- * @example type HelloOutput = RouterOutputs['example']['hello']
- */
+*
+* @example type HelloOutput = RouterOutputs['example']['hello']
+*/
 export type RouterOutputs = inferRouterOutputs<AppRouter>;
+export type GetGlucoseLogsOutput = RouterOutputs['glucose']['getLogs']
 
 export function TRPCReactProvider(props: { children: React.ReactNode }) {
 	const queryClient = getQueryClient();


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 8**

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

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

`21-25`: **Add fallback and input validation to the utility function**

The `removeSearchParam` utility function is well implemented for removing specific query parameters. However, consider adding fallbacks and validation:

1. Add a fallback for when NEXT_PUBLIC_APP_URL is not available
2. Add validation for the pathname parameter to prevent runtime errors


```diff
export function removeSearchParam(pathname: string, param: string) {
-  const urlObj = new URL(`${process.env.NEXT_PUBLIC_APP_URL}${pathname}`)
+  if (!pathname) return '';
+  const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 
+    (typeof window !== 'undefined' ? window.location.origin : '');
+  const urlObj = new URL(`${baseUrl}${pathname}`)
  urlObj.searchParams.delete(param)
  return urlObj.pathname + urlObj.search
}
```

</blockquote></details>
<details>
<summary>src/server/api/routers/glucose.ts (2)</summary><blockquote>

`51-56`: **Remove redundant else clause**

The else clause is redundant as the previous branch will always throw, preventing execution from reaching this point.


```diff
 if (error instanceof Error) {
     throw new TRPCError({
         code: "INTERNAL_SERVER_ERROR",
         message: error.message
     })
-} else {
-    throw new TRPCError({
-        code: "INTERNAL_SERVER_ERROR",
-        message: "An unknown error occurred"
-    })
 }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 51-56: This else clause can be omitted because previous branches break early.

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

---

`36-58`: **Consider abstracting error handling**

The error handling logic is duplicated between this procedure and the `create` procedure.


Consider extracting the error handling into a reusable function:

```typescript
function handleTRPCError(error: unknown): never {
  if (error instanceof TRPCError) throw error;

  throw new TRPCError({
    code: "INTERNAL_SERVER_ERROR",
    message: error instanceof Error ? error.message : "An unknown error occurred"
  });
}

// Then in both procedures:
try {
  // procedure code
} catch (error) {
  handleTRPCError(error);
}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 51-56: This else clause can be omitted because previous branches break early.

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/charts/glucose/glucose-history.tsx (2)</summary><blockquote>

`9-55`: **The chart implementation is well-structured but could benefit from Y-axis configuration**

The GlucoseHistoryChart is well-implemented with good attention to detail in the X-axis configuration, tooltip, and line styling. However, unlike the GlucoseDistributionChart, this component is missing a Y-axis configuration which would help users understand glucose values without having to hover for tooltip data.


Consider adding a Y-axis configuration similar to the one in the GlucoseDistributionChart:

```diff
            <Line
                dataKey="glucose"
                type="natural"
                stroke="var(--chart-5)"
                strokeWidth={2}
                dot={false}
            />
+           <YAxis
+               dataKey="glucose"
+               tickLine={false}
+               axisLine={false}
+               tickMargin={8}
+           />
```

---

`22-49`: **Consider handling empty data state**

The chart doesn't appear to handle cases where the `glucoseLogs` array might be empty, which could lead to an empty chart display with no feedback to the user.


Consider checking for empty data and displaying a message:

```diff
        <CardContent>
+           {glucoseLogs.length === 0 ? (
+               <div className="flex h-[200px] items-center justify-center text-muted-foreground">
+                   No data available for the selected period
+               </div>
+           ) : (
            <ChartContainer config={chartConfig}>
                <LineChart
                    accessibilityLayer
                    data={glucoseLogs}
                    margin={{
                        left: 12,
                        right: 12,
                    }}
                >
                    {/* Chart components */}
                </LineChart>
            </ChartContainer>
+           )}
        </CardContent>
```

</blockquote></details>
<details>
<summary>src/app/(home)/logs/glucose/page.tsx (1)</summary><blockquote>

`28-28`: **Consider more explicit type handling for timePeriod**

The current type casting approach could be more explicit.


Consider using a more explicit type handling approach:

```diff
- <GlucoseHistory timePeriod={timePeriod as string ?? ''} />
+ <GlucoseHistory timePeriod={typeof timePeriod === 'string' ? timePeriod : ''} />
```

</blockquote></details>
<details>
<summary>src/components/charts/glucose/glucose-distribution.tsx (2)</summary><blockquote>

`3-3`: **Remove trailing comma from import statement**

There's a trailing comma after `type ChartConfig` which should be removed.


```diff
- import { ChartTooltipContent, ChartTooltip, type ChartConfig, } from "~/components/ui/chart"
+ import { ChartTooltipContent, ChartTooltip, type ChartConfig } from "~/components/ui/chart"
```

---

`5-8`: **Consolidate duplicate imports**

The `Card` component is imported twice - once in line 7 and once as part of the import in line 6.


```diff
import { CartesianGrid, Scatter, ScatterChart, XAxis, YAxis } from "recharts"
import { ChartContainer } from "~/components/ui/chart"
- import { CardContent, CardHeader, CardTitle } from "~/components/ui/card"
- import { Card } from "~/components/ui/card"
+ import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
import type { GetGlucoseLogsOutput } from "~/trpc/react"
```

</blockquote></details>
<details>
<summary>src/components/logs/common/filter-logs.tsx (4)</summary><blockquote>

`14-65`: **Good implementation of the filter component!**

The FilterLogs component looks well-structured, implementing a slide-over panel for filtering glucose level logs by time period. The UI is clean and user-friendly.

One minor issue to note is the naming inconsistency between this component's name (FilterLogs) and how it's imported in glucose.tsx as "FilterGlucoseLogs" (line 15 of that file). Consider aligning these names for better clarity.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 38-38: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

---

[error] 46-46: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

---

[error] 52-52: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

</details>

</details>

---

`38-41`: **Replace template literals with string literals**

You're using template literals for simple strings, which is unnecessary since there's no interpolation happening.

```diff
-                                <Link href={`/logs/glucose`} className={cn(buttonVariants({
+                                <Link href="/logs/glucose" className={cn(buttonVariants({
                                    variant: "outline",
                                    size: 'sm'
                                }), "size-full")}>
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 38-38: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

</details>

</details>

---

`46-49`: **Replace template literals with string literals**

Same issue here - template literals are unnecessary for simple strings.

```diff
-                                <Link href={`/logs/glucose?timePeriod=lastWeek`} className={cn(buttonVariants({
+                                <Link href="/logs/glucose?timePeriod=lastWeek" className={cn(buttonVariants({
                                    variant: "outline",
                                    size: 'sm'
                                }), "size-full")}>
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 46-46: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

</details>

</details>

---

`52-55`: **Replace template literals with string literals**

Same issue here - template literals are unnecessary for simple strings.

```diff
-                                <Link href={`/logs/glucose?timePeriod=lastMonth`} className={cn(buttonVariants({
+                                <Link href="/logs/glucose?timePeriod=lastMonth" className={cn(buttonVariants({
                                    variant: "outline",
                                    size: 'sm'
                                }), "size-full")}>
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 52-52: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/ui/badge.tsx (1)</summary><blockquote>

`1-3`: **Use `import type` for type-only imports**

When importing modules only used as types, it's better to use `import type` to ensure they're properly removed from the final bundle.

```diff
-import * as React from "react"
+import type * as React from "react"
 import { Slot } from "@radix-ui/react-slot"
 import { cva, type VariantProps } from "class-variance-authority"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/ui/dialog.tsx (1)</summary><blockquote>

`3-5`: **Use `import type` for type-only imports**

When importing modules only used as types, it's better to use `import type` to ensure they're properly removed from the final bundle.

```diff
-import * as React from "react"
+import type * as React from "react"
 import * as DialogPrimitive from "@radix-ui/react-dialog"
 import { XIcon } from "lucide-react"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 3-3: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/logs/history/glucose.tsx (5)</summary><blockquote>

`10-12`: **Use `import type` for type-only imports**

When importing types, it's better to use `import type` to ensure they're properly removed from the final bundle.

```diff
-import {
-    type ChartConfig
-} from "~/components/ui/chart"
+import type { ChartConfig } from "~/components/ui/chart"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

---

`15-16`: **Fix component naming inconsistency**

There's a naming mismatch between the import and the actual component name. The component is defined as `FilterLogs` in its file but imported as `FilterGlucoseLogs` here.

```diff
-import FilterGlucoseLogs from "../common/filter-logs"
+import FilterLogs from "../common/filter-logs"
```

Make sure to update all references to `FilterGlucoseLogs` in this file as well.

---

`80-87`: **Simplify boolean logic in className conditions**

The triple negation (`!!!timePeriodLabel`) is unnecessarily complex and can be simplified.

```diff
            <div className={cn("flex flex-col sm:flex-row items-start sm:items-center gap-2", {
-                "justify-end": !!!timePeriodLabel,
+                "justify-end": !timePeriodLabel,
                "justify-between": !!timePeriodLabel,
            })}>
```

Additionally, you could further simplify to just check presence/absence:
```diff
            <div className={cn("flex flex-col sm:flex-row items-start sm:items-center gap-2", {
-                "justify-end": !timePeriodLabel,
-                "justify-between": !!timePeriodLabel,
+                "justify-end": !timePeriodLabel,
+                "justify-between": timePeriodLabel !== null,
            })}>
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 82-82: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

</details>

</details>

---

`86-86`: **Update component reference to match import name**

If you update the import name as suggested earlier, you'll need to update this reference as well.

```diff
-                <FilterGlucoseLogs tooltipContent="Filter glucose levels" />
+                <FilterLogs tooltipContent="Filter glucose levels" />
```

---

`88-98`: **Review grid layout for improved responsiveness**

The current grid layout has a good structure for large screens, but you might want to review how charts are arranged on medium-sized screens. Consider adding a medium breakpoint (md) to adjust the layout between mobile and large screens.

</blockquote></details>
<details>
<summary>src/components/ui/chart.tsx (1)</summary><blockquote>

`131-165`: **Memo dependency completeness**

`tooltipLabel` depends on `nameKey` (used via `labelKey ?? item?.dataKey …`).  
Add `nameKey` to the dependency array to avoid stale renders when the prop changes.

```diff
-    config,
+    config,
     labelKey,
+    nameKey,
```

</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 0fe377d11187a65c219a3677ccc559b489859963 and 05273dc42ab4256649abdf105c8c66f97154a671.

</details>

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

* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

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

* `package.json` (1 hunks)
* `src/app/(home)/layout.tsx` (1 hunks)
* `src/app/(home)/logs/glucose/page.tsx` (1 hunks)
* `src/components/charts/glucose/glucose-distribution.tsx` (1 hunks)
* `src/components/charts/glucose/glucose-history.tsx` (1 hunks)
* `src/components/charts/glucose/glucose-pie.tsx` (1 hunks)
* `src/components/logs/common/filter-labels.tsx` (1 hunks)
* `src/components/logs/common/filter-logs.tsx` (1 hunks)
* `src/components/logs/forms/glucose.tsx` (3 hunks)
* `src/components/logs/history/glucose.tsx` (1 hunks)
* `src/components/ui/badge.tsx` (1 hunks)
* `src/components/ui/chart.tsx` (1 hunks)
* `src/components/ui/dialog.tsx` (1 hunks)
* `src/env.js` (2 hunks)
* `src/lib/utils.ts` (1 hunks)
* `src/server/api/routers/glucose.ts` (2 hunks)
* `src/trpc/react.tsx` (1 hunks)

</details>

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

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

<details>
<summary>src/components/logs/forms/glucose.tsx (1)</summary><blockquote>

<details>
<summary>src/lib/time-picker-utils.ts (1)</summary>

* `toISOStringWithTimezone` (214-222)

</details>

</blockquote></details>
<details>
<summary>src/trpc/react.tsx (1)</summary><blockquote>

<details>
<summary>src/server/api/root.ts (1)</summary>

* `AppRouter` (15-15)

</details>

</blockquote></details>
<details>
<summary>src/app/(home)/logs/glucose/page.tsx (2)</summary><blockquote>

<details>
<summary>src/trpc/react.tsx (1)</summary>

* `api` (25-25)

</details>
<details>
<summary>src/components/ui/tabs.tsx (4)</summary>

* `Tabs` (66-66)
* `TabsList` (66-66)
* `TabsTrigger` (66-66)
* `TabsContent` (66-66)

</details>

</blockquote></details>
<details>
<summary>src/components/charts/glucose/glucose-pie.tsx (3)</summary><blockquote>

<details>
<summary>src/trpc/react.tsx (1)</summary>

* `GetGlucoseLogsOutput` (40-40)

</details>
<details>
<summary>src/components/ui/chart.tsx (4)</summary>

* `ChartConfig` (11-19)
* `ChartContainer` (347-347)
* `ChartTooltip` (348-348)
* `ChartTooltipContent` (349-349)

</details>
<details>
<summary>src/components/ui/card.tsx (5)</summary>

* `Card` (85-85)
* `CardHeader` (86-86)
* `CardTitle` (88-88)
* `CardDescription` (90-90)
* `CardContent` (91-91)

</details>

</blockquote></details>
<details>
<summary>src/components/logs/common/filter-labels.tsx (3)</summary><blockquote>

<details>
<summary>src/components/ui/badge.tsx (1)</summary>

* `Badge` (46-46)

</details>
<details>
<summary>src/lib/utils.ts (2)</summary>

* `removeSearchParam` (21-25)
* `cn` (4-6)

</details>
<details>
<summary>src/components/ui/button.tsx (1)</summary>

* `buttonVariants` (59-59)

</details>

</blockquote></details>
<details>
<summary>src/components/charts/glucose/glucose-history.tsx (3)</summary><blockquote>

<details>
<summary>src/trpc/react.tsx (1)</summary>

* `GetGlucoseLogsOutput` (40-40)

</details>
<details>
<summary>src/components/ui/chart.tsx (4)</summary>

* `ChartConfig` (11-19)
* `ChartContainer` (347-347)
* `ChartTooltip` (348-348)
* `ChartTooltipContent` (349-349)

</details>
<details>
<summary>src/components/ui/card.tsx (4)</summary>

* `Card` (85-85)
* `CardHeader` (86-86)
* `CardTitle` (88-88)
* `CardContent` (91-91)

</details>

</blockquote></details>
<details>
<summary>src/components/ui/dialog.tsx (1)</summary><blockquote>

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

* `cn` (4-6)

</details>

</blockquote></details>
<details>
<summary>src/components/logs/common/filter-logs.tsx (4)</summary><blockquote>

<details>
<summary>src/components/ui/sheet.tsx (6)</summary>

* `Sheet` (131-131)
* `SheetTrigger` (132-132)
* `SheetContent` (134-134)
* `SheetHeader` (135-135)
* `SheetTitle` (137-137)
* `SheetDescription` (138-138)

</details>
<details>
<summary>src/components/ui/tooltip.tsx (3)</summary>

* `Tooltip` (61-61)
* `TooltipTrigger` (61-61)
* `TooltipContent` (61-61)

</details>
<details>
<summary>src/components/ui/button.tsx (2)</summary>

* `Button` (59-59)
* `buttonVariants` (59-59)

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

* `cn` (4-6)

</details>

</blockquote></details>
<details>
<summary>src/components/logs/history/glucose.tsx (6)</summary><blockquote>

<details>
<summary>src/components/ui/chart.tsx (1)</summary>

* `ChartConfig` (11-19)

</details>
<details>
<summary>src/components/ui/skeleton.tsx (1)</summary>

* `Skeleton` (13-13)

</details>
<details>
<summary>src/trpc/react.tsx (1)</summary>

* `api` (25-25)

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

* `cn` (4-6)

</details>
<details>
<summary>src/components/charts/glucose/glucose-history.tsx (1)</summary>

* `GlucoseHistoryChart` (9-55)

</details>
<details>
<summary>src/components/charts/glucose/glucose-distribution.tsx (1)</summary>

* `GlucoseDistributionChart` (11-57)

</details>

</blockquote></details>
<details>
<summary>src/components/ui/badge.tsx (1)</summary><blockquote>

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

* `cn` (4-6)

</details>

</blockquote></details>
<details>
<summary>src/components/ui/chart.tsx (1)</summary><blockquote>

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

* `cn` (4-6)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/server/api/routers/glucose.ts</summary>

[error] 51-56: This else clause can be omitted because previous branches break early.

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>
<details>
<summary>src/components/ui/dialog.tsx</summary>

[error] 3-3: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>
<details>
<summary>src/components/logs/common/filter-logs.tsx</summary>

[error] 38-38: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

---

[error] 46-46: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

---

[error] 52-52: Do not use template literals if interpolation and special-character handling are not needed.

Unsafe fix: Replace with string literal


(lint/style/noUnusedTemplateLiteral)

</details>
<details>
<summary>src/components/logs/history/glucose.tsx</summary>

[error] 82-82: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

---

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

---

[error] 63-63: This hook does not specify all of its dependencies: data

This dependency is not specified in the hook dependency list.


(lint/correctness/useExhaustiveDependencies)

---

[error] 63-63: This hook does not specify all of its dependencies: data?.filter

This dependency is not specified in the hook dependency list.

This dependency is not specified in the hook dependency list.


(lint/correctness/useExhaustiveDependencies)

</details>
<details>
<summary>src/components/ui/badge.tsx</summary>

[error] 1-1: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>
<details>
<summary>src/components/ui/chart.tsx</summary>

[error] 83-83: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

</details>

</details>

</details>

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

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

`52-52`: **Good addition of the Recharts library**

Adding the Recharts library is appropriate for implementing the glucose log visualization features. This popular React charting library will support the new chart components mentioned in the AI summary.

</details>
<details>
<summary>src/env.js (2)</summary>

`30-30`: **Properly configured client-side environment variable**

The addition of `NEXT_PUBLIC_APP_URL` to the client schema with proper URL validation is well implemented.

---

`39-39`: **Correct mapping of the environment variable**

The environment variable is correctly mapped from process.env to the runtime environment.

</details>
<details>
<summary>src/app/(home)/layout.tsx (1)</summary>

`12-12`: **Code formatting improvement**

Removed extra whitespace for better code style and readability.

</details>
<details>
<summary>src/trpc/react.tsx (2)</summary>

`36-38`: **Good formatting update**

The JSDoc formatting change improves readability while maintaining the documentation's purpose.

---

`40-40`: **Well-defined type export**

Exporting `GetGlucoseLogsOutput` as a reusable type promotes type safety and consistency across components that consume glucose log data. This is a good TypeScript practice.

</details>
<details>
<summary>src/components/logs/forms/glucose.tsx (3)</summary>

`166-166`: **Improved responsive design**

Removing the fixed width constraint allows the date picker button to adapt to its container width, creating a more consistent responsive layout across device sizes.

---

`188-192`: **Good defensive programming**

Adding a null check before attempting to process the date prevents potential runtime errors when the date is undefined or null. This is a good defensive programming practice.

---

`237-237`: **More descriptive button label**

Changing from "Submit" to "Upload log" provides clearer context about the action being performed, improving the user experience.

</details>
<details>
<summary>src/components/charts/glucose/glucose-pie.tsx (3)</summary>

`1-21`: **Good component structure and imports**

The component correctly imports the necessary dependencies and types, including the recharts library for visualization and UI components for layout.

---

`22-38`: **Well-typed props and config**

The props interface is clean and uses the shared `GetGlucoseLogsOutput` type. The chart configuration with the `satisfies ChartConfig` ensures type safety.

---

`40-53`: **Effective data processing**

The code efficiently processes glucose logs using reduce to categorize values as high (>144) or low (<72), following standard medical thresholds.

</details>
<details>
<summary>src/server/api/routers/glucose.ts (1)</summary>

`1-2`: **Appropriate imports**

These imports are needed for the new `getLogs` procedure: `TRPCError` for error handling and `eq` for filtering in the database query.

</details>
<details>
<summary>src/components/logs/common/filter-labels.tsx (1)</summary>

`1-25`: **Good implementation of the filter labels component!**

The component correctly handles the display of filter labels with a dismissible interface, using appropriate UI components and styling. The use of the `removeSearchParam` utility function for URL manipulation and the `Link` component for navigation is well implemented.

</details>
<details>
<summary>src/app/(home)/logs/glucose/page.tsx (1)</summary>

`8-15`: **Good implementation of async server component**

The conversion to an async server component is well done. The prefetching of glucose logs data is a good practice for performance.

</details>
<details>
<summary>src/components/charts/glucose/glucose-distribution.tsx (1)</summary>

`11-57`: **Well-implemented scatter chart**

The GlucoseDistributionChart is well-implemented with good attention to detail in axis configuration, tooltip, and scatter point styling. The inclusion of both X and Y axes provides good context for the data.

</details>
<details>
<summary>src/components/ui/badge.tsx (2)</summary>

`7-26`: **Well-structured badge variants configuration**

The badge variants are well-defined with appropriate styling for different use cases (default, secondary, destructive, outline). The use of class-variance-authority provides a clean way to manage variants.

---

`28-44`: **Good implementation of the Badge component**

The Badge component is well-implemented, supporting the `asChild` pattern for component composition and properly handling variant props. The use of the Slot component allows for flexible rendering while maintaining the badge styling.

</details>
<details>
<summary>src/components/ui/dialog.tsx (4)</summary>

`9-31`: **Well-implemented dialog component wrappers**

The Dialog, DialogTrigger, DialogPortal, and DialogClose components are properly implemented as wrappers around Radix UI primitives, with appropriate data attributes for slot identification.

---

`33-47`: **Good implementation of the dialog overlay**

The DialogOverlay component is well-structured with appropriate animation classes and styling for the background overlay.

---

`49-73`: **Well-structured dialog content implementation**

The DialogContent component properly combines DialogPortal and DialogOverlay with content rendering and styling. The close button is accessible with a screen reader label and appropriate focus handling.

---

`75-122`: **Good implementation of dialog layout components**

The DialogHeader, DialogFooter, DialogTitle, and DialogDescription components provide good structure and styling for dialog content organization, with appropriate responsive behavior.

</details>
<details>
<summary>src/components/logs/history/glucose.tsx (1)</summary>

`26-55`: **Good skeleton implementation for loading state**

The skeleton component is well-structured, matching the layout of the actual content and providing a good loading experience.

</details>
<details>
<summary>src/components/ui/chart.tsx (1)</summary>

`205-213`: **Tailwind arbitrary-value syntax typo**

`border-(--color-border)` and `bg-(--color-bg)` are not valid arbitrary-value variants; the correct Tailwind syntax is `border-[--color-border]` and `bg-[--color-bg]`.  
Without the square brackets Tailwind will not generate the classes, leaving the element unstyled.

```diff
-                          "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
+                          "shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
```
  
Please confirm you have a Tailwind plugin that intentionally supports the parenthesis notation; otherwise the above change is required.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T10:03:03Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 3**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>src/server/api/routers/glucose.ts (1)</summary><blockquote>

`38-42`: **Consider adding more fields to the query**

The current selection only includes id, glucose, and date, which may be insufficient for the display needs of the chart components.



```diff
 return await db.select({
     id: glucoseLog.id,
     glucose: glucoseLog.glucose,
     date: glucoseLog.date,
+    type: glucoseLog.type,
+    units: glucoseLog.units,
 }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date).limit(100)
```

</blockquote></details>

</blockquote></details>

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

<details>
<summary>src/server/api/routers/glucose.ts (2)</summary><blockquote>

`36-58`: **Consider refactoring error handling to reduce duplication**

The error handling pattern in the `getLogs` procedure is duplicated from the `create` procedure. Consider extracting this to a reusable utility function to promote DRY principles.


```diff
+ import { handleTRPCError } from "~/lib/error-utils"; // Add this file if it doesn't exist

  getLogs: protectedProcedure.query(async ({ ctx: { db, auth } }) => {
      try {
          return await db.select({
              id: glucoseLog.id,
              glucose: glucoseLog.glucose,
              date: glucoseLog.date,
          }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date).limit(100)
-     } catch (error) {
-         if (error instanceof TRPCError) throw error;
-
-         if (error instanceof Error) {
-             throw new TRPCError({
-                 code: "INTERNAL_SERVER_ERROR",
-                 message: error.message
-             })
-         } else {
-             throw new TRPCError({
-                 code: "INTERNAL_SERVER_ERROR",
-                 message: "An unknown error occurred"
-             })
-         }
+     } catch (error) {
+         handleTRPCError(error);
      }
  })
```

In a new file `src/lib/error-utils.ts`:
```typescript
import { TRPCError } from "@trpc/server";

export function handleTRPCError(error: unknown): never {
  if (error instanceof TRPCError) throw error;

  if (error instanceof Error) {
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: error.message
    });
  }
  
  throw new TRPCError({
    code: "INTERNAL_SERVER_ERROR",
    message: "An unknown error occurred"
  });
}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 51-56: This else clause can be omitted because previous branches break early.

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

---

`51-56`: **Remove unnecessary else clause**

Since the previous if statement throws an error, the else clause is redundant.


```diff
  if (error instanceof Error) {
      throw new TRPCError({
          code: "INTERNAL_SERVER_ERROR",
          message: error.message
      })
-  } else {
-      throw new TRPCError({
-          code: "INTERNAL_SERVER_ERROR",
-          message: "An unknown error occurred"
-      })
  }
+ throw new TRPCError({
+     code: "INTERNAL_SERVER_ERROR",
+     message: "An unknown error occurred"
+ })
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 51-56: This else clause can be omitted because previous branches break early.

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/logs/history/glucose.tsx (4)</summary><blockquote>

`10-12`: **Use `import type` for type-only imports**

When importing types, it's better to use the `import type` syntax to ensure they're removed during compilation.


```diff
-import {
-    type ChartConfig
-} from "~/components/ui/chart"
+import type { ChartConfig } from "~/components/ui/chart"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

---

`82-82`: **Simplify boolean expression by removing triple negation**

The triple negation (`!!!`) is unnecessarily complex and can be simplified for better readability.


```diff
-                "justify-end": !!!timePeriodLabel,
+                "justify-end": !timePeriodLabel,
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 82-82: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

</details>

</details>

---

`89-91`: **Grid layout inconsistency for GlucoseHistoryChart**

The grid layout for charts appears inconsistent. The first chart (`GlucoseHistoryChart`) spans 1 column while in the skeleton it spans 2 columns.


```diff
-            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
-                <div className="lg:col-span-1">
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-2">
                     <GlucoseHistoryChart glucoseLogs={glucoseLogs ?? []} chartConfig={chartConfig} />
                 </div>
```

For comparison, in the skeleton:
```
<div className="lg:col-span-2">
    <div className="mt-2">
        <Skeleton className="h-8 w-48 mb-4" />
        <Skeleton className="h-[300px] w-full" />
    </div>
</div>
```

---

`73-73`: **Consider extracting time period label logic to a utility function**

The time period label logic could be extracted to a reusable utility function to make the code more maintainable and reusable.


```diff
+    const getTimePeriodLabel = (period: string): string | null => {
+        if (period === "lastWeek") return "Last week";
+        if (period === "lastMonth") return "Last month";
+        return null;
+    }
+
-    const timePeriodLabel = timePeriod === "lastWeek" ? "Last week" : timePeriod === "lastMonth" ? "Last month" : null
+    const timePeriodLabel = getTimePeriodLabel(timePeriod)
```

</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 05273dc42ab4256649abdf105c8c66f97154a671 and 10670dcf32450153b9c1d998df84b4c94a322812.

</details>

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

* `src/components/charts/glucose/glucose-distribution.tsx` (1 hunks)
* `src/components/charts/glucose/glucose-history.tsx` (1 hunks)
* `src/components/charts/glucose/glucose-pie.tsx` (1 hunks)
* `src/components/logs/history/glucose.tsx` (1 hunks)
* `src/components/ui/chart.tsx` (1 hunks)
* `src/server/api/routers/glucose.ts` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (4)</summary>

* src/components/charts/glucose/glucose-pie.tsx
* src/components/charts/glucose/glucose-distribution.tsx
* src/components/charts/glucose/glucose-history.tsx
* src/components/ui/chart.tsx

</details>

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

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/components/logs/history/glucose.tsx</summary>

[error] 82-82: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

---

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>
<details>
<summary>src/server/api/routers/glucose.ts</summary>

[error] 51-56: This else clause can be omitted because previous branches break early.

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T10:18:38Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>src/components/logs/history/glucose.tsx (1)</summary><blockquote>

`76-78`: _🛠️ Refactor suggestion_

**Improve error handling to prevent component rendering on error**

Currently, the component shows an error toast but then continues execution, which could lead to rendering issues if data is undefined. Consider adding a dedicated error state UI component instead of just showing a toast.


```diff
    if (error) {
        toast.error("Error fetching glucose logs")
+       return (
+           <div className="flex flex-col items-center justify-center py-8">
+               <p className="text-destructive font-medium">Failed to load glucose logs</p>
+               <p className="text-muted-foreground text-sm">Please try again later</p>
+           </div>
+       )
    }
```

</blockquote></details>

</blockquote></details>

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

<details>
<summary>src/components/logs/history/glucose.tsx (4)</summary><blockquote>

`101-101`: **Simplify the boolean expression by removing redundant double-negation**

Using triple negation (`!!!`) is unnecessary and harder to read. A single negation is clearer and performs the same function.


```diff
-                "justify-end": !!!timePeriodLabel,
+                "justify-end": !timePeriodLabel,
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 101-101: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

</details>

</details>

---

`10-12`: **Use `import type` for type-only imports**

The ChartConfig import is only used as a type, not as a value. Using `import type` ensures that it's removed during compilation and avoids loading unnecessary modules.


```diff
-import {
-    type ChartConfig
-} from "~/components/ui/chart"
+import type { ChartConfig } from "~/components/ui/chart"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

---

`76-77`: **Add more specific error message in the toast**

The current error message is generic. Including error details would help users understand what went wrong and potentially report the issue.


```diff
-        toast.error("Error fetching glucose logs")
+        toast.error(`Error fetching glucose logs: ${error.message}`)
```

---

`86-96`: **Enhance empty state UI with actionable guidance**

The empty state is minimalistic. Consider adding a more visually appealing and actionable empty state to guide users.


```diff
    if (glucoseLogs.length === 0) {
        return (
-            <div className="flex flex-col">
-                <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
-                    <p className="text-sm text-muted-foreground">
-                        Start tracking your glucose levels to see them here.
-                    </p>
-                </div>
+            <div className="flex flex-col items-center justify-center p-8 text-center">
+                <p className="text-lg font-medium mb-2">No glucose logs found</p>
+                <p className="text-muted-foreground text-sm mb-4">
+                    {timePeriodLabel ? `No data available for ${timePeriodLabel.toLowerCase()}.` : 'Start tracking your glucose levels to see them here.'}
+                </p>
+                {/* Consider adding a button to add new glucose log */}
             </div>
        )
    }
```

</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 10670dcf32450153b9c1d998df84b4c94a322812 and 1ab01f4acfb4b5e8f95737a9517e18b3e39cd05a.

</details>

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

* `src/components/logs/history/glucose.tsx` (1 hunks)

</details>

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

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/components/logs/history/glucose.tsx</summary>

[error] 101-101: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

---

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T10:27:39Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>src/components/logs/history/glucose.tsx (1)</summary><blockquote>

`76-79`: _🛠️ Refactor suggestion_

**Improve error handling to prevent rendering with errors.**

The current error handling shows a toast but continues with rendering, which might lead to unexpected UI if data is unavailable. Consider adding a return statement to show an error UI instead.

```diff
    if (error) {
        toast.error("Error fetching glucose logs")
        console.error(error)
+       return (
+           <div className="flex flex-col items-center justify-center py-8">
+               <p className="text-destructive font-medium">Failed to load glucose logs</p>
+               <p className="text-muted-foreground text-sm">Please try again later</p>
+           </div>
+       )
    }
```

</blockquote></details>

</blockquote></details>

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

<details>
<summary>src/components/auth/forms/sign-up.tsx (1)</summary><blockquote>

`54-54`: **Remove debugging console log.**

This console log statement appears to be debugging code that should be removed before production. It logs all form errors on every render which is unnecessary for users and may expose sensitive information.

```diff
-    console.log(form.state.errors)
```

</blockquote></details>
<details>
<summary>src/components/logs/history/glucose.tsx (4)</summary><blockquote>

`10-12`: **Use `import type` for type-only imports.**

When importing symbols that are only used as types, it's better to use `import type` to ensure they're removed during compilation, reducing bundle size.

```diff
-import {
-    type ChartConfig
-} from "~/components/ui/chart"
+import type {
+    ChartConfig
+} from "~/components/ui/chart"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

---

`87-97`: **Enhance the empty state with more guidance for users.**

The current empty state provides minimal information. Consider enhancing it with a button to add a new glucose log or more specific guidance.

```diff
    if (glucoseLogs.length === 0) {
        return (
            <div className="flex flex-col">
-                <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <div className="flex flex-col items-center justify-center p-8 text-center">
+                    <p className="text-lg font-medium mb-2">No glucose logs found</p>
                     <p className="text-sm text-muted-foreground">
                         Start tracking your glucose levels to see them here.
                     </p>
+                    {/* Consider adding a button to add a new glucose log */}
+                    {/* <Button className="mt-4" asChild>
+                        <Link href="/logs/glucose/add">Add Glucose Log</Link>
+                    </Button> */}
                </div>
            </div>
        )
    }
```

---

`102-102`: **Simplify boolean logic to improve readability.**

The triple negation (`!!!timePeriodLabel`) is unnecessarily complex and hard to read. Using a single negation or a direct boolean check would be clearer.

```diff
-                "justify-end": !!!timePeriodLabel,
+                "justify-end": !timePeriodLabel,
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 102-102: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

</details>

</details>

---

`108-118`: **Consider adding aria-labels for better accessibility.**

The chart components are missing accessibility attributes. Consider adding aria-labels to improve screen reader support for the visualizations.

```diff
    <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
-        <div className="lg:col-span-1">
+        <div className="lg:col-span-1" aria-label="Glucose history chart">
            <GlucoseHistoryChart glucoseLogs={glucoseLogs ?? []} chartConfig={chartConfig} />
        </div>
-        <div className="lg:col-span-1">
+        <div className="lg:col-span-1" aria-label="Glucose distribution chart">
            <GlucoseDistributionChart glucoseLogs={glucoseLogs ?? []} chartConfig={chartConfig} />
        </div>
-        <div className="lg:col-span-1">
+        <div className="lg:col-span-1" aria-label="Glucose pie chart">
            <GlucosePieChart glucoseLogs={glucoseLogs ?? []} />
        </div>
    </div>
```

</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 1ab01f4acfb4b5e8f95737a9517e18b3e39cd05a and 551020b66f391079ff2538348d8679887b651c0a.

</details>

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

* `src/components/auth/forms/sign-in.tsx` (0 hunks)
* `src/components/auth/forms/sign-up.tsx` (5 hunks)
* `src/components/logs/history/glucose.tsx` (1 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* src/components/auth/forms/sign-in.tsx

</details>

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

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

<details>
<summary>src/components/auth/forms/sign-up.tsx (2)</summary><blockquote>

<details>
<summary>src/components/ui/checkbox.tsx (1)</summary>

* `Checkbox` (32-32)

</details>
<details>
<summary>src/components/ui/label.tsx (1)</summary>

* `Label` (24-24)

</details>

</blockquote></details>
<details>
<summary>src/components/logs/history/glucose.tsx (6)</summary><blockquote>

<details>
<summary>src/components/ui/chart.tsx (1)</summary>

* `ChartConfig` (11-19)

</details>
<details>
<summary>src/components/ui/skeleton.tsx (1)</summary>

* `Skeleton` (13-13)

</details>
<details>
<summary>src/trpc/react.tsx (2)</summary>

* `api` (25-25)
* `GetGlucoseLogsOutput` (40-40)

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

* `cn` (4-6)

</details>
<details>
<summary>src/components/charts/glucose/glucose-history.tsx (1)</summary>

* `GlucoseHistoryChart` (9-64)

</details>
<details>
<summary>src/components/charts/glucose/glucose-distribution.tsx (1)</summary>

* `GlucoseDistributionChart` (11-66)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/components/auth/forms/sign-up.tsx</summary>

[error] 42-42: expected `,` but instead found `onError`

Remove onError

(parse)

---

[error] 183-183: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

</details>
<details>
<summary>src/components/logs/history/glucose.tsx</summary>

[error] 102-102: Avoid redundant double-negation.

It is not necessary to use double-negation when a value will already be coerced to a boolean.
Unsafe fix: Remove redundant double-negation


(lint/complexity/noExtraBooleanCast)

---

[error] 10-12: All these imports are only used as types.

Importing the types with import type ensures that they are removed by the compilers and avoids loading unnecessary modules.
Safe fix: Use import type.


(lint/style/useImportType)

</details>

</details>

</details>

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

<details>
<summary>src/components/auth/forms/sign-up.tsx (1)</summary>

`180-194`: **LGTM: Checkbox implementation with proper accessibility.**

The checkbox implementation follows best practices with proper labeling and use of the controlled component pattern. The checkbox state is correctly managed by the form system.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 183-183: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

</details>

</details>

</details>
<details>
<summary>src/components/logs/history/glucose.tsx (2)</summary>

`64-74`: **LGTM: Filter implementation with proper dependency handling.**

The useEffect hook correctly filters glucose logs based on the time period with proper dependencies. Good job handling the case when data is undefined with the early return.

---

`34-53`: **The skeleton layout should match the actual component layout.**

The skeleton component uses the same grid layout as the actual component which is good for preventing layout shifts. However, ensure that any future changes to the main component layout are also reflected in the skeleton.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T10:45:17Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>src/components/auth/forms/sign-up.tsx (2)</summary><blockquote>

`25-25`: **Remove unused `rememberMe` field**

The `rememberMe` field is defined in your form's default values, but it's never used in the form submission logic. The `onSubmit` handler only extracts `email`, `password`, `firstName`, and `lastName` without using the `rememberMe` value.

---

`179-193`: _⚠️ Potential issue_

**Either implement or remove the Remember Me functionality**

You've added a UI checkbox for "Remember me" but the value isn't used in the sign-up process. The `onSubmit` handler doesn't extract or use the `rememberMe` value when calling `signUp.email()`.


Either:
1. Implement the functionality by using the value in the sign-up process:
```diff
-            const { email, password, firstName, lastName } = value
+            const { email, password, firstName, lastName, rememberMe } = value
 
             await signUp.email({
                 email,
                 password,
-                name: `${firstName} ${lastName}`
+                name: `${firstName} ${lastName}`,
+                rememberMe
             }, {
```

2. Or remove the unused checkbox:
```diff
-            <div className="flex items-center gap-2">
-                <form.Field
-                    name="rememberMe"
-                    children={(field) => (
-                        <>
-                            <Checkbox
-                                id="remember"
-                                checked={field.state.value}
-                                onCheckedChange={(checked) => field.handleChange(checked === true)}
-                            />
-                            <Label htmlFor="remember">Remember me</Label>
-                        </>
-                    )}
-                />
-            </div>
```

Make sure to also remove it from defaultValues if you choose option 2.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 182-182: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

</details>

</details>

</blockquote></details>

</blockquote></details>

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

<details>
<summary>src/components/auth/forms/sign-up.tsx (2)</summary><blockquote>

`53-54`: **Remove debugging console.log statement**

This console log statement outputs form validation errors on every render, which will clutter the browser console in production. Debugging code should be removed before deploying to production.

```diff
-    console.log(form.state.errors)
-
```

---

`157-157`: **Update the label's htmlFor attribute**

The label for the password confirmation field has an incorrect htmlFor attribute. It should point to the password_confirmation input.

```diff
-                <Label htmlFor="password">Confirm Password</Label>
+                <Label htmlFor="password_confirmation">Confirm Password</Label>
```

</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 551020b66f391079ff2538348d8679887b651c0a and 7d30c7ca7d494e618b79bbe4b45af757f65faae0.

</details>

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

* `src/components/auth/forms/sign-up.tsx` (4 hunks)

</details>

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

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

<details>
<summary>src/components/auth/forms/sign-up.tsx (2)</summary><blockquote>

<details>
<summary>src/components/ui/checkbox.tsx (1)</summary>

* `Checkbox` (32-32)

</details>
<details>
<summary>src/components/ui/label.tsx (1)</summary>

* `Label` (24-24)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/components/auth/forms/sign-up.tsx</summary>

[error] 182-182: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-13T11:02:39Z

Review comments:
diff_hunk: @@ -0,0 +1,81 @@
+'use client'
+
+import type { GetGlucoseLogsOutput } from '~/trpc/react'
+
+import { Pie, PieChart } from "recharts"
+
+import {
+    Card,
+    CardContent,
+    CardDescription,
+    CardHeader,
+    CardTitle
+} from "~/components/ui/card"
+
+import {
+    ChartContainer,
+    ChartTooltip,
+    ChartTooltipContent,
+    type ChartConfig
+} from "~/components/ui/chart"
+
+type Props = {
+    glucoseLogs: GetGlucoseLogsOutput
+}
+
+const chartConfig = {
+    value: {
+        label: "Value"
+    },
+    high: {
+        color: "hsl(var(--chart-1))",
+        label: "High"
+    },
+    low: {
+        color: "hsl(var(--chart-2))",
+        label: "Low"
+    }
+} satisfies ChartConfig
+
+const GlucosePieChart = ({ glucoseLogs }: Props) => {
+    const highVLows = glucoseLogs.reduce((acc, log) => {
+        if (log.glucose > 144) {
+            acc.high += 1
+        } else if (log.glucose < 72) {
+            acc.low += 1
+        }
+        return acc
+    }, { high: 0, low: 0 })
+
+    const chartData = [
+        { name: "high", value: highVLows.high },
+        { name: "low", value: highVLows.low },
+    ]
+
+    return (
+        <Card className="flex flex-col">
+            <CardHeader className="items-center pb-0">
+                <CardTitle>
+                    Glucose high & low distribution
+                </CardTitle>
+                <CardDescription>January - June 2024</CardDescription>
path: src/components/charts/glucose/glucose-pie.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Replace hardcoded date range**

The hardcoded date range "January - June 2024" will become outdated and makes the component less reusable.


Consider using a dynamic date range based on the actual data:

```diff
- <CardDescription>January - June 2024</CardDescription>
+ <CardDescription>
+   {glucoseLogs.length > 0 
+     ? `${new Date(glucoseLogs[0].date).toLocaleDateString()} - ${new Date(glucoseLogs[glucoseLogs.length-1].date).toLocaleDateString()}`
+     : "No data available"}
+ </CardDescription>
```

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:01Z
author_association: NONE
diff_hunk: @@ -0,0 +1,81 @@
+'use client'
+
+import type { GetGlucoseLogsOutput } from '~/trpc/react'
+
+import { Pie, PieChart } from "recharts"
+
+import {
+    Card,
+    CardContent,
+    CardDescription,
+    CardHeader,
+    CardTitle
+} from "~/components/ui/card"
+
+import {
+    ChartContainer,
+    ChartTooltip,
+    ChartTooltipContent,
+    type ChartConfig
+} from "~/components/ui/chart"
+
+type Props = {
+    glucoseLogs: GetGlucoseLogsOutput
+}
+
+const chartConfig = {
+    value: {
+        label: "Value"
+    },
+    high: {
+        color: "hsl(var(--chart-1))",
+        label: "High"
+    },
+    low: {
+        color: "hsl(var(--chart-2))",
+        label: "Low"
+    }
+} satisfies ChartConfig
+
+const GlucosePieChart = ({ glucoseLogs }: Props) => {
+    const highVLows = glucoseLogs.reduce((acc, log) => {
+        if (log.glucose > 144) {
+            acc.high += 1
+        } else if (log.glucose < 72) {
+            acc.low += 1
+        }
+        return acc
+    }, { high: 0, low: 0 })
+
+    const chartData = [
+        { name: "high", value: highVLows.high },
+        { name: "low", value: highVLows.low },
+    ]
+
+    return (
+        <Card className="flex flex-col">
+            <CardHeader className="items-center pb-0">
+                <CardTitle>
+                    Glucose high & low distribution
+                </CardTitle>
+                <CardDescription>January - June 2024</CardDescription>
+            </CardHeader>
+            <CardContent className="flex-1 pb-0">
+                <ChartContainer
+                    config={chartConfig} 
+                    className="mx-auto aspect-square max-h-[250px]"
+                >
+                    <PieChart>
+                        <ChartTooltip
+                            cursor={false}
+                            content={<ChartTooltipContent hideLabel />}
+                        />
+                        <Pie data={chartData} dataKey="value" nameKey="name" />
+                    </PieChart>
+                </ChartContainer>
+            </CardContent>
+        </Card>
+    )
path: src/components/charts/glucose/glucose-pie.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add handling for empty data**

The component doesn't handle the case when there's no data to display. This could lead to an empty or confusing chart.


Add a check for empty data and display a meaningful message:

```diff
 return (
     <Card className="flex flex-col">
         <CardHeader className="items-center pb-0">
             <CardTitle>
                 Glucose high & low distribution
             </CardTitle>
             <CardDescription>January - June 2024</CardDescription>
         </CardHeader>
         <CardContent className="flex-1 pb-0">
+            {chartData.every(item => item.value === 0) ? (
+                <div className="flex h-[250px] items-center justify-center text-muted-foreground">
+                    No glucose data to display
+                </div>
+            ) : (
             <ChartContainer
                 config={chartConfig} 
                 className="mx-auto aspect-square max-h-[250px]"
             >
                 <PieChart>
                     <ChartTooltip
                         cursor={false}
                         content={<ChartTooltipContent hideLabel />}
                     />
                     <Pie data={chartData} dataKey="value" nameKey="name" />
                 </PieChart>
             </ChartContainer>
+            )}
         </CardContent>
     </Card>
 )
```


<!-- 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
    return (
        <Card className="flex flex-col">
            <CardHeader className="items-center pb-0">
                <CardTitle>
                    Glucose high & low distribution
                </CardTitle>
                <CardDescription>January - June 2024</CardDescription>
            </CardHeader>
            <CardContent className="flex-1 pb-0">
                {chartData.every(item => item.value === 0) ? (
                    <div className="flex h-[250px] items-center justify-center text-muted-foreground">
                        No glucose data to display
                    </div>
                ) : (
                    <ChartContainer
                        config={chartConfig}
                        className="mx-auto aspect-square max-h-[250px]"
                    >
                        <PieChart>
                            <ChartTooltip
                                cursor={false}
                                content={<ChartTooltipContent hideLabel />}
                            />
                            <Pie data={chartData} dataKey="value" nameKey="name" />
                        </PieChart>
                    </ChartContainer>
                )}
            </CardContent>
        </Card>
    )
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:01Z
author_association: NONE
diff_hunk: @@ -32,4 +33,27 @@ export const glucoseRouter = createTRPCRouter({
             }
         }
     }),
+    getLogs: protectedProcedure.query(async ({ ctx: { db, auth } }) => {
+        try {
+            return await db.select({
+                id: glucoseLog.id,
+                glucose: glucoseLog.glucose,
+                date: glucoseLog.date,
+            }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date)
+        } catch (error) {
path: src/server/api/routers/glucose.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider adding more fields to the query**

The current selection only includes id, glucose, and date, which may be insufficient for the display needs of the chart components.


Consider including additional fields like `type` and `units` which would be useful for display:

```diff
 return await db.select({
     id: glucoseLog.id,
     glucose: glucoseLog.glucose,
     date: glucoseLog.date,
+    type: glucoseLog.type,
+    units: glucoseLog.units,
 }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date)
```


<!-- 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
    getLogs: protectedProcedure.query(async ({ ctx: { db, auth } }) => {
        try {
            return await db.select({
                id:    glucoseLog.id,
                glucose: glucoseLog.glucose,
                date:  glucoseLog.date,
                type:  glucoseLog.type,
                units: glucoseLog.units,
            })
            .from(glucoseLog)
            .where(eq(glucoseLog.userId, auth.user.id))
            .orderBy(glucoseLog.date)
        } catch (error) {
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-13T10:03:02Z
author_association: NONE
diff_hunk: @@ -32,4 +33,27 @@ export const glucoseRouter = createTRPCRouter({
             }
         }
     }),
+    getLogs: protectedProcedure.query(async ({ ctx: { db, auth } }) => {
+        try {
+            return await db.select({
+                id: glucoseLog.id,
+                glucose: glucoseLog.glucose,
+                date: glucoseLog.date,
+            }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date)
+        } catch (error) {
path: src/server/api/routers/glucose.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add pagination for performance**

The query fetches all records for a user without any limit, which could lead to performance issues if there are many records.


Consider adding pagination or a limit:

```diff
- }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date)
+ }).from(glucoseLog).where(eq(glucoseLog.userId, auth.user.id)).orderBy(glucoseLog.date).limit(100)
```

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,103 @@
+"use client"
+
+import { api } from '~/trpc/react'
+
+import dayjs from "dayjs"
+import { usePathname } from "next/navigation"
+import { useEffect, useState } from "react"
+import { GlucoseDistributionChart } from "~/components/charts/glucose/glucose-distribution"
+import { GlucoseHistoryChart } from "~/components/charts/glucose/glucose-history"
+import {
+    type ChartConfig
+} from "~/components/ui/chart"
+import { cn } from "~/lib/utils"
+import FilterLabels from "../common/filter-labels"
+import FilterGlucoseLogs from "../common/filter-logs"
+import GlucosePieChart from '~/components/charts/glucose/glucose-pie'
+import { Skeleton } from "~/components/ui/skeleton"
+
+const chartConfig = {
+    glucose: {
+        label: "Glucose",
+        color: "hsl(var(--chart-5))",
+    }
+} satisfies ChartConfig
+
+const GlucoseHistorySkeleton = () => {
+    return (
+        <div className="flex flex-col">
+            <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <Skeleton className="h-8 w-32" />
+                <Skeleton className="h-8 w-8" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-2">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+            </div>
+        </div>
+    )
+}
+
+const GlucoseHistory = ({ timePeriod }: { timePeriod: string }) => {
+    const { data, isLoading: isGlucoseLogsLoading } = api.glucose.getLogs.useQuery()
+
+    const [glucoseLogs, setGlucoseLogs] = useState(data)
+    const pathname = usePathname()
+
+    useEffect(() => {
+        if (timePeriod === "lastWeek") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
+        } else if (timePeriod === "lastMonth") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
+        } else {
+            setGlucoseLogs(data)
+        }
+    }, [timePeriod])
path: src/components/logs/history/glucose.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Add missing dependencies to useEffect**

The useEffect hook is missing the `data` dependency, which could lead to stale data when the data changes. Add it to ensure the filter is reapplied when data updates.

```diff
    useEffect(() => {
        if (timePeriod === "lastWeek") {
            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
        } else if (timePeriod === "lastMonth") {
            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
        } else {
            setGlucoseLogs(data)
        }
-    }, [timePeriod])
+    }, [timePeriod, data])
```


<!-- 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
    useEffect(() => {
        if (timePeriod === "lastWeek") {
            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
        } else if (timePeriod === "lastMonth") {
            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
        } else {
            setGlucoseLogs(data)
        }
    }, [timePeriod, data])
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 63-63: This hook does not specify all of its dependencies: data

This dependency is not specified in the hook dependency list.


(lint/correctness/useExhaustiveDependencies)

---

[error] 63-63: This hook does not specify all of its dependencies: data?.filter

This dependency is not specified in the hook dependency list.

This dependency is not specified in the hook dependency list.


(lint/correctness/useExhaustiveDependencies)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,353 @@
+"use client"
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "~/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+  [k in string]: {
+    label?: React.ReactNode
+    icon?: React.ComponentType
+  } & (
+    | { color?: string; theme?: never }
+    | { color?: never; theme: Record<keyof typeof THEMES, string> }
+  )
+}
+
+type ChartContextProps = {
+  config: ChartConfig
+}
+
+const ChartContext = React.createContext<ChartContextProps | null>(null)
+
+function useChart() {
+  const context = React.useContext(ChartContext)
+
+  if (!context) {
+    throw new Error("useChart must be used within a <ChartContainer />")
+  }
+
+  return context
+}
+
+function ChartContainer({
+  id,
+  className,
+  children,
+  config,
+  ...props
+}: React.ComponentProps<"div"> & {
+  config: ChartConfig
+  children: React.ComponentProps<
+    typeof RechartsPrimitive.ResponsiveContainer
+  >["children"]
+}) {
+  const uniqueId = React.useId()
+  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+  return (
+    <ChartContext.Provider value={{ config }}>
+      <div
+        data-slot="chart"
+        data-chart={chartId}
+        className={cn(
+          "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
+          className
+        )}
+        {...props}
+      >
+        <ChartStyle id={chartId} config={config} />
+        <RechartsPrimitive.ResponsiveContainer>
+          {children}
+        </RechartsPrimitive.ResponsiveContainer>
+      </div>
+    </ChartContext.Provider>
+  )
+}
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+  const colorConfig = Object.entries(config).filter(
+    ([, config]) => config.theme || config.color
+  )
+
+  if (!colorConfig.length) {
+    return null
+  }
+
+  return (
+    <style
+      dangerouslySetInnerHTML={{
+        __html: Object.entries(THEMES)
+          .map(
+            ([theme, prefix]) => `
+${prefix} [data-chart=${id}] {
+${colorConfig
+  .map(([key, itemConfig]) => {
+    const color =
+      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
+      itemConfig.color
+    return color ? `  --color-${key}: ${color};` : null
+  })
+  .join("\n")}
+}
+`
+          )
+          .join("\n"),
+      }}
+    />
+  )
+}
+
+const ChartTooltip = RechartsPrimitive.Tooltip
+
+function ChartTooltipContent({
+  active,
+  payload,
+  className,
+  indicator = "dot",
+  hideLabel = false,
+  hideIndicator = false,
+  label,
+  labelFormatter,
+  labelClassName,
+  formatter,
+  color,
+  nameKey,
+  labelKey,
+}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
+  React.ComponentProps<"div"> & {
+    hideLabel?: boolean
+    hideIndicator?: boolean
+    indicator?: "line" | "dot" | "dashed"
+    nameKey?: string
+    labelKey?: string
+  }) {
+  const { config } = useChart()
+
+  const tooltipLabel = React.useMemo(() => {
+    if (hideLabel || !payload?.length) {
+      return null
+    }
+
+    const [item] = payload
+    const key = `${labelKey || item?.dataKey || item?.name || "value"}`
+    const itemConfig = getPayloadConfigFromPayload(config, item, key)
+    const value =
+      !labelKey && typeof label === "string"
+        ? config[label as keyof typeof config]?.label || label
+        : itemConfig?.label
+
+    if (labelFormatter) {
+      return (
+        <div className={cn("font-medium", labelClassName)}>
+          {labelFormatter(value, payload)}
+        </div>
+      )
+    }
+
+    if (!value) {
+      return null
+    }
+
+    return <div className={cn("font-medium", labelClassName)}>{value}</div>
+  }, [
+    label,
+    labelFormatter,
+    payload,
+    hideLabel,
+    labelClassName,
+    config,
+    labelKey,
+  ])
+
+  if (!active || !payload?.length) {
+    return null
+  }
+
+  const nestLabel = payload.length === 1 && indicator !== "dot"
+
+  return (
+    <div
+      className={cn(
+        "border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
+        className
+      )}
+    >
+      {!nestLabel ? tooltipLabel : null}
+      <div className="grid gap-1.5">
+        {payload.map((item, index) => {
+          const key = `${nameKey || item.name || item.dataKey || "value"}`
+          const itemConfig = getPayloadConfigFromPayload(config, item, key)
+          const indicatorColor = color || item.payload.fill || item.color
+
+          return (
+            <div
+              key={item.dataKey}
+              className={cn(
+                "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
+                indicator === "dot" && "items-center"
+              )}
+            >
+              {formatter && item?.value !== undefined && item.name ? (
+                formatter(item.value, item.name, item, index, item.payload)
+              ) : (
+                <>
+                  {itemConfig?.icon ? (
+                    <itemConfig.icon />
+                  ) : (
+                    !hideIndicator && (
+                      <div
+                        className={cn(
+                          "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
+                          {
+                            "h-2.5 w-2.5": indicator === "dot",
+                            "w-1": indicator === "line",
+                            "w-0 border-[1.5px] border-dashed bg-transparent":
+                              indicator === "dashed",
+                            "my-0.5": nestLabel && indicator === "dashed",
+                          }
+                        )}
+                        style={
+                          {
+                            "--color-bg": indicatorColor,
+                            "--color-border": indicatorColor,
+                          } as React.CSSProperties
+                        }
+                      />
+                    )
+                  )}
+                  <div
+                    className={cn(
+                      "flex flex-1 justify-between leading-none",
+                      nestLabel ? "items-end" : "items-center"
+                    )}
+                  >
+                    <div className="grid gap-1.5">
+                      {nestLabel ? tooltipLabel : null}
+                      <span className="text-muted-foreground">
+                        {itemConfig?.label || item.name}
+                      </span>
+                    </div>
+                    {item.value && (
+                      <span className="text-foreground font-mono font-medium tabular-nums">
+                        {item.value.toLocaleString()}
+                      </span>
+                    )}
path: src/components/ui/chart.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**`0` values disappear from the tooltip**

The current truthy check treats `0` as *falsy*, so a reading of “0 mg/dL” (or any legitimate zero) is not rendered.

```diff
-                    {item.value && (
+                    {item.value !== undefined && item.value !== null && (
```
This displays zero while still hiding `undefined`/`null`.



<!-- 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
                    {item.value !== undefined && item.value !== null && (
                      <span className="text-foreground font-mono font-medium tabular-nums">
                        {item.value.toLocaleString()}
                      </span>
                    )}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,353 @@
+"use client"
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "~/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+  [k in string]: {
+    label?: React.ReactNode
+    icon?: React.ComponentType
+  } & (
+    | { color?: string; theme?: never }
+    | { color?: never; theme: Record<keyof typeof THEMES, string> }
+  )
+}
+
+type ChartContextProps = {
+  config: ChartConfig
+}
+
+const ChartContext = React.createContext<ChartContextProps | null>(null)
+
+function useChart() {
+  const context = React.useContext(ChartContext)
+
+  if (!context) {
+    throw new Error("useChart must be used within a <ChartContainer />")
+  }
+
+  return context
+}
+
+function ChartContainer({
+  id,
+  className,
+  children,
+  config,
+  ...props
+}: React.ComponentProps<"div"> & {
+  config: ChartConfig
+  children: React.ComponentProps<
+    typeof RechartsPrimitive.ResponsiveContainer
+  >["children"]
+}) {
+  const uniqueId = React.useId()
+  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+  return (
path: src/components/ui/chart.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Sanitise `chartId` to avoid breaking the generated CSS selector**

`id` supplied by a consumer might contain characters that have special meaning inside an attribute selector (e.g. `:` or `]`).  
Because the selector is later interpolated into raw CSS (`[data-chart=${id}]`) without quotes or escaping, such characters would break the rule or even open the door for malicious CSS injection.

```diff
-  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+  // Strip anything that is not alphanumeric, dash or underscore so the value
+  // remains a valid unquoted CSS attribute-value.
+  const safeId = (id ?? uniqueId).replace(/[^\w-]/g, "")
+  const chartId = `chart-${safeId}`
```
 <!-- risk: malformed CSS / potential CSS injection -->


<!-- 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 uniqueId = React.useId()
  // Strip anything that is not alphanumeric, dash or underscore so the value
  // remains a valid unquoted CSS attribute-value.
  const safeId = (id ?? uniqueId).replace(/[^\w-]/g, "")
  const chartId = `chart-${safeId}`

  return (
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,353 @@
+"use client"
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "~/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+  [k in string]: {
+    label?: React.ReactNode
+    icon?: React.ComponentType
+  } & (
+    | { color?: string; theme?: never }
+    | { color?: never; theme: Record<keyof typeof THEMES, string> }
+  )
+}
+
+type ChartContextProps = {
+  config: ChartConfig
+}
+
+const ChartContext = React.createContext<ChartContextProps | null>(null)
+
+function useChart() {
+  const context = React.useContext(ChartContext)
+
+  if (!context) {
+    throw new Error("useChart must be used within a <ChartContainer />")
+  }
+
+  return context
+}
+
+function ChartContainer({
+  id,
+  className,
+  children,
+  config,
+  ...props
+}: React.ComponentProps<"div"> & {
+  config: ChartConfig
+  children: React.ComponentProps<
+    typeof RechartsPrimitive.ResponsiveContainer
+  >["children"]
+}) {
+  const uniqueId = React.useId()
+  const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+  return (
+    <ChartContext.Provider value={{ config }}>
+      <div
+        data-slot="chart"
+        data-chart={chartId}
+        className={cn(
+          "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
+          className
+        )}
+        {...props}
+      >
+        <ChartStyle id={chartId} config={config} />
+        <RechartsPrimitive.ResponsiveContainer>
+          {children}
+        </RechartsPrimitive.ResponsiveContainer>
+      </div>
+    </ChartContext.Provider>
+  )
+}
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+  const colorConfig = Object.entries(config).filter(
+    ([, config]) => config.theme || config.color
+  )
+
+  if (!colorConfig.length) {
+    return null
+  }
+
+  return (
+    <style
+      dangerouslySetInnerHTML={{
+        __html: Object.entries(THEMES)
+          .map(
+            ([theme, prefix]) => `
+${prefix} [data-chart=${id}] {
+${colorConfig
+  .map(([key, itemConfig]) => {
+    const color =
+      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
+      itemConfig.color
+    return color ? `  --color-${key}: ${color};` : null
+  })
+  .join("\n")}
+}
+`
+          )
+          .join("\n"),
+      }}
+    />
+  )
+}
path: src/components/ui/chart.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Replace `dangerouslySetInnerHTML` with a safer alternative**

Using `dangerouslySetInnerHTML` for generating a `<style>` tag is flagged by static-analysis because it bypasses React’s XSS protections.  
Here the content is generated from user-controlled `config` values (`color` strings) so CSS-based attacks (e.g. `url("javascript:...")`) are theoretically possible.

React can safely render the same string as a child node – no HTML parsing is required – so `dangerouslySetInnerHTML` is not necessary.

```diff
-  return (
-    <style
-      dangerouslySetInnerHTML={{
-        __html: Object.entries(THEMES)
-          .map(
-            ([theme, prefix]) => `
-${prefix} [data-chart=${id}] {
-${colorConfig
-  .map(([key, itemConfig]) => {
-    const color =
-      itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
-      itemConfig.color
-    return color ? `  --color-${key}: ${color};` : null
-  })
-  .join("\n")}
-}
-`
-          )
-          .join("\n"),
-      }}
-    />
-  )
+  const css = Object.entries(THEMES)
+    .map(([theme, prefix]) => {
+      const vars = colorConfig
+        .map(([key, itemConfig]) => {
+          const color =
+            itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
+            itemConfig.color
+          return color ? `  --color-${key}: ${color};` : null
+        })
+        .filter(Boolean)
+        .join("\n")
+      return `${prefix} [data-chart=${id}] {\n${vars}\n}`
+    })
+    .join("\n")
+
+  return <style>{css}</style>
+}
```
This keeps the exact output while restoring React’s built-in escaping.  
If dynamic CSS ever needs truly unescaped content, consider sanitising inputs with `CSS.escape` beforehand.
 <!-- security / XSS -->


<!-- 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 css = Object.entries(THEMES)
      .map(([theme, prefix]) => {
        const vars = colorConfig
          .map(([key, itemConfig]) => {
            const color =
              itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
              itemConfig.color
            return color ? `  --color-${key}: ${color};` : null
          })
          .filter(Boolean)
          .join("\n")
        return `${prefix} [data-chart=${id}] {\n${vars}\n}`
      })
      .join("\n")

    return <style>{css}</style>
  }
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 83-83: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 10670dc
created_at: 2025-05-13T10:03:02Z
author_association: NONE
diff_hunk: @@ -0,0 +1,103 @@
+"use client"
+
+import { api } from '~/trpc/react'
+
+import dayjs from "dayjs"
+import { usePathname } from "next/navigation"
+import { useEffect, useState } from "react"
+import { GlucoseDistributionChart } from "~/components/charts/glucose/glucose-distribution"
+import { GlucoseHistoryChart } from "~/components/charts/glucose/glucose-history"
+import {
+    type ChartConfig
+} from "~/components/ui/chart"
+import { cn } from "~/lib/utils"
+import FilterLabels from "../common/filter-labels"
+import FilterGlucoseLogs from "../common/filter-logs"
+import GlucosePieChart from '~/components/charts/glucose/glucose-pie'
+import { Skeleton } from "~/components/ui/skeleton"
+
+const chartConfig = {
+    glucose: {
+        label: "Glucose",
+        color: "hsl(var(--chart-5))",
+    }
+} satisfies ChartConfig
+
+const GlucoseHistorySkeleton = () => {
+    return (
+        <div className="flex flex-col">
+            <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <Skeleton className="h-8 w-32" />
+                <Skeleton className="h-8 w-8" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-2">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+            </div>
+        </div>
+    )
+}
+
+const GlucoseHistory = ({ timePeriod }: { timePeriod: string }) => {
+    const { data, isLoading: isGlucoseLogsLoading } = api.glucose.getLogs.useQuery()
+
+    const [glucoseLogs, setGlucoseLogs] = useState(data)
+    const pathname = usePathname()
+
+    useEffect(() => {
+        if (timePeriod === "lastWeek") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
+        } else if (timePeriod === "lastMonth") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
+        } else {
+            setGlucoseLogs(data)
+        }
+    }, [timePeriod, data])
+
+    const timePeriodLabel = timePeriod === "lastWeek" ? "Last week" : timePeriod === "lastMonth" ? "Last month" : null
+
+    if (isGlucoseLogsLoading) {
+        return <GlucoseHistorySkeleton />
+    }
path: src/components/logs/history/glucose.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Check for empty data state**

There's no handling for when the data is empty, which could lead to empty charts being displayed. Consider adding a message for this scenario.


```diff
    if (isGlucoseLogsLoading) {
        return <GlucoseHistorySkeleton />
    }
+
+    // Check if there's no data to display
+    if (!data || data.length === 0) {
+        return (
+            <div className="flex flex-col items-center justify-center p-8 text-center">
+                <p className="text-lg font-medium mb-2">No glucose logs found</p>
+                <p className="text-muted-foreground text-sm mb-4">Start tracking your glucose levels to see them here.</p>
+                {/* Consider adding a link or button to add a new glucose log */}
+            </div>
+        );
+    }
```


<!-- 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
    if (isGlucoseLogsLoading) {
        return <GlucoseHistorySkeleton />
    }

    // Check if there's no data to display
    if (!data || data.length === 0) {
        return (
            <div className="flex flex-col items-center justify-center p-8 text-center">
                <p className="text-lg font-medium mb-2">No glucose logs found</p>
                <p className="text-muted-foreground text-sm mb-4">
                    Start tracking your glucose levels to see them here.
                </p>
                {/* Consider adding a link or button to add a new glucose log */}
            </div>
        )
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 1ab01f4
created_at: 2025-05-13T10:18:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,103 @@
+"use client"
+
+import { api } from '~/trpc/react'
+
+import dayjs from "dayjs"
+import { usePathname } from "next/navigation"
+import { useEffect, useState } from "react"
+import { GlucoseDistributionChart } from "~/components/charts/glucose/glucose-distribution"
+import { GlucoseHistoryChart } from "~/components/charts/glucose/glucose-history"
+import {
+    type ChartConfig
+} from "~/components/ui/chart"
+import { cn } from "~/lib/utils"
+import FilterLabels from "../common/filter-labels"
+import FilterGlucoseLogs from "../common/filter-logs"
+import GlucosePieChart from '~/components/charts/glucose/glucose-pie'
+import { Skeleton } from "~/components/ui/skeleton"
+
+const chartConfig = {
+    glucose: {
+        label: "Glucose",
+        color: "hsl(var(--chart-5))",
+    }
+} satisfies ChartConfig
+
+const GlucoseHistorySkeleton = () => {
+    return (
+        <div className="flex flex-col">
+            <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <Skeleton className="h-8 w-32" />
+                <Skeleton className="h-8 w-8" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-2">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+            </div>
+        </div>
+    )
+}
+
+const GlucoseHistory = ({ timePeriod }: { timePeriod: string }) => {
+    const { data, isLoading: isGlucoseLogsLoading } = api.glucose.getLogs.useQuery()
+
+    const [glucoseLogs, setGlucoseLogs] = useState(data)
+    const pathname = usePathname()
+
+    useEffect(() => {
+        if (timePeriod === "lastWeek") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
+        } else if (timePeriod === "lastMonth") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
+        } else {
+            setGlucoseLogs(data)
+        }
+    }, [timePeriod, data])
+
+    const timePeriodLabel = timePeriod === "lastWeek" ? "Last week" : timePeriod === "lastMonth" ? "Last month" : null
+
+    if (isGlucoseLogsLoading) {
+        return <GlucoseHistorySkeleton />
+    }
path: src/components/logs/history/glucose.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add error handling for the data fetching operation**

The component doesn't handle API errors, which could lead to a poor user experience if the data fetch fails.


```diff
-    const { data, isLoading: isGlucoseLogsLoading } = api.glucose.getLogs.useQuery()
+    const { data, isLoading: isGlucoseLogsLoading, error } = api.glucose.getLogs.useQuery()
     
     // ... existing code ...

-    if (isGlucoseLogsLoading) {
+    if (error) {
+        return (
+            <div className="flex flex-col items-center justify-center py-8">
+                <p className="text-destructive font-medium">Failed to load glucose logs</p>
+                <p className="text-muted-foreground text-sm">{error.message}</p>
+            </div>
+        )
+    } else if (isGlucoseLogsLoading) {
         return <GlucoseHistorySkeleton />
     }
```


<!-- 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 { data, isLoading: isGlucoseLogsLoading, error } = api.glucose.getLogs.useQuery()

    const [glucoseLogs, setGlucoseLogs] = useState(data)
    const pathname = usePathname()

    useEffect(() => {
        if (timePeriod === "lastWeek") {
            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
        } else if (timePeriod === "lastMonth") {
            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
        } else {
            setGlucoseLogs(data)
        }
    }, [timePeriod, data])

    const timePeriodLabel = timePeriod === "lastWeek"
        ? "Last week"
        : timePeriod === "lastMonth"
            ? "Last month"
            : null

    if (error) {
        return (
            <div className="flex flex-col items-center justify-center py-8">
                <p className="text-destructive font-medium">Failed to load glucose logs</p>
                <p className="text-muted-foreground text-sm">{error.message}</p>
            </div>
        )
    } else if (isGlucoseLogsLoading) {
        return <GlucoseHistorySkeleton />
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-13T10:18:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,103 @@
+"use client"
+
+import { api } from '~/trpc/react'
+
+import dayjs from "dayjs"
+import { usePathname } from "next/navigation"
+import { useEffect, useState } from "react"
+import { GlucoseDistributionChart } from "~/components/charts/glucose/glucose-distribution"
+import { GlucoseHistoryChart } from "~/components/charts/glucose/glucose-history"
+import {
+    type ChartConfig
+} from "~/components/ui/chart"
+import { cn } from "~/lib/utils"
+import FilterLabels from "../common/filter-labels"
+import FilterGlucoseLogs from "../common/filter-logs"
+import GlucosePieChart from '~/components/charts/glucose/glucose-pie'
+import { Skeleton } from "~/components/ui/skeleton"
+
+const chartConfig = {
+    glucose: {
+        label: "Glucose",
+        color: "hsl(var(--chart-5))",
+    }
+} satisfies ChartConfig
+
+const GlucoseHistorySkeleton = () => {
+    return (
+        <div className="flex flex-col">
+            <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <Skeleton className="h-8 w-32" />
+                <Skeleton className="h-8 w-8" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-2">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+                <div className="lg:col-span-1">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
+            </div>
+        </div>
+    )
+}
+
+const GlucoseHistory = ({ timePeriod }: { timePeriod: string }) => {
+    const { data, isLoading: isGlucoseLogsLoading } = api.glucose.getLogs.useQuery()
+
+    const [glucoseLogs, setGlucoseLogs] = useState(data)
+    const pathname = usePathname()
+
+    useEffect(() => {
+        if (timePeriod === "lastWeek") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
+        } else if (timePeriod === "lastMonth") {
+            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
+        } else {
+            setGlucoseLogs(data)
+        }
+    }, [timePeriod, data])
path: src/components/logs/history/glucose.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Initialize state with data and handle null/undefined data better**

Your current implementation initializes `glucoseLogs` with `data` which is undefined on first render, and your filtering logic doesn't handle undefined data well.


```diff
-    const [glucoseLogs, setGlucoseLogs] = useState(data)
+    const [glucoseLogs, setGlucoseLogs] = useState<typeof data>([])

     useEffect(() => {
+        if (!data) return;
+        
         if (timePeriod === "lastWeek") {
-            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
+            setGlucoseLogs(data.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
         } else if (timePeriod === "lastMonth") {
-            setGlucoseLogs(data?.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
+            setGlucoseLogs(data.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
         } else {
             setGlucoseLogs(data)
         }
     }, [timePeriod, data])
```


<!-- 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 [glucoseLogs, setGlucoseLogs] = useState<typeof data>([])
    const pathname = usePathname()

    useEffect(() => {
        if (!data) return;

        if (timePeriod === "lastWeek") {
            setGlucoseLogs(data.filter((log) => dayjs(log.date).isSame(dayjs(), "week")))
        } else if (timePeriod === "lastMonth") {
            setGlucoseLogs(data.filter((log) => dayjs(log.date).isSame(dayjs(), "month")))
        } else {
            setGlucoseLogs(data)
        }
    }, [timePeriod, data])
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 1ab01f4
created_at: 2025-05-13T10:18:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,122 @@
+"use client"
+
+import { api, type GetGlucoseLogsOutput } from '~/trpc/react'
+
+import dayjs from "dayjs"
+import { usePathname } from "next/navigation"
+import { useEffect, useState } from "react"
+import { GlucoseDistributionChart } from "~/components/charts/glucose/glucose-distribution"
+import { GlucoseHistoryChart } from "~/components/charts/glucose/glucose-history"
+import {
+    type ChartConfig
+} from "~/components/ui/chart"
+import { cn } from "~/lib/utils"
+import FilterLabels from "../common/filter-labels"
+import FilterGlucoseLogs from "../common/filter-logs"
+import GlucosePieChart from '~/components/charts/glucose/glucose-pie'
+import { Skeleton } from "~/components/ui/skeleton"
+import { toast } from 'sonner'
+
+const chartConfig = {
+    glucose: {
+        label: "Glucose",
+        color: "hsl(var(--chart-5))",
+    }
+} satisfies ChartConfig
+
+const GlucoseHistorySkeleton = () => {
+    return (
+        <div className="flex flex-col">
+            <div className="flex flex-col sm:flex-row items-start sm:items-center gap-2">
+                <Skeleton className="h-8 w-32" />
+                <Skeleton className="h-8 w-8" />
+            </div>
+            <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
+                <div className="lg:col-span-2">
+                    <div className="mt-2">
+                        <Skeleton className="h-8 w-48 mb-4" />
+                        <Skeleton className="h-[300px] w-full" />
+                    </div>
+                </div>
path: src/components/logs/history/glucose.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Grid layout inconsistency between skeleton and actual component**

The skeleton uses `lg:col-span-2` for the first chart, while the actual component uses `lg:col-span-1`. This will cause a layout shift when the content loads.


```diff
// In GlucoseHistorySkeleton
-                <div className="lg:col-span-2">
+                <div className="lg:col-span-1">
                     <div className="mt-2">
                         <Skeleton className="h-8 w-48 mb-4" />
                         <Skeleton className="h-[300px] w-full" />
                     </div>
                 </div>
```


Also applies to: 107-110

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 551020b
created_at: 2025-05-13T10:27:39Z
author_association: NONE
diff_hunk: @@ -36,9 +38,10 @@ const SignUpForm = () => {
                 email,
                 password,
                 name: `${firstName} ${lastName}`
-            }, {
+            }, {p
path: src/components/auth/forms/sign-up.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix the syntax error in the options object.**

There's a syntax error in the options object passed to `signUp.email()`. The `{p` is invalid syntax and will cause a JavaScript error.

```diff
-            }, {p
+            }, {
```


<!-- 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
            }, {
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 7d30c7c
created_at: 2025-05-13T10:45:16Z
author_association: NONE
diff_hunk: @@ -21,6 +22,7 @@ const SignUpForm = () => {
             email: "",
             password: "",
             confirmPassword: "",
+            rememberMe: false
path: src/components/auth/forms/sign-up.tsx
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify if rememberMe field is actually used.**

You've added a `rememberMe` field to the form's default values, but it's not being used in the form submission logic. The `onSubmit` handler only extracts `email`, `password`, `firstName`, and `lastName` without using the `rememberMe` value.

You should either:
1. Use the `rememberMe` value in authentication logic
2. Remove it if it's not needed

Run this to verify if `rememberMe` is used in authentication:

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if rememberMe is used in the auth client or related files
rg -A 5 "rememberMe" src/lib/auth-client.ts
```

Length of output: 43

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for any references to `rememberMe` across the entire codebase
rg -n "rememberMe" .

# Also check for underscore variant just in case
rg -n "remember_me" .
```

Length of output: 575

---



</details>

**Remove unused `rememberMe` from Sign-Up form**

The `rememberMe` field is defined in your Zod schema and used in the Sign-In flow, but in `src/components/auth/forms/sign-up.tsx` it’s never read or passed to any API. You should either wire it into your sign-up logic or remove it to avoid confusion.

Locations to update:
- src/components/auth/forms/sign-up.tsx
  - Line 25: drop the default `rememberMe: false`
  - Line 182: remove the `<Checkbox name="rememberMe" …/>` JSX

Suggested diff:
```diff
--- a/src/components/auth/forms/sign-up.tsx
+++ b/src/components/auth/forms/sign-up.tsx
@@ –22,7 +22,6 @@
   defaultValues: {
     firstName: '',
     lastName: '',
-    rememberMe: false,
     email: '',
     password: '',
   },
@@ –179,7 +178,6 @@
           name="lastName"
         />
-        <Checkbox name="rememberMe" label="Remember me" />
         <Button type="submit">Sign Up</Button>
       </form>
     }
```


<!-- 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
// src/components/auth/forms/sign-up.tsx

export function SignUpForm() {
  const methods = useForm({
    resolver: zodResolver(signUpSchema),
    defaultValues: {
      firstName: '',
      lastName: '',
      email: '',
      password: '',
    },
  });

  const onSubmit = (data: SignUpData) => {
    // your sign-up logic here, no more rememberMe field
  };

  return (
    <form onSubmit={methods.handleSubmit(onSubmit)}>
      <Controller
        name="firstName"
        control={methods.control}
        render={({ field }) => <Input {...field} label="First name" />}
      />
      <Controller
        name="lastName"
        control={methods.control}
        render={({ field }) => <Input {...field} label="Last name" />}
      />
      <Controller
        name="email"
        control={methods.control}
        render={({ field }) => <Input {...field} label="Email" />}
      />
      <Controller
        name="password"
        control={methods.control}
        render={({ field }) => <Input {...field} label="Password" type="password" />}
      />
      <Button type="submit">Sign Up</Button>
    </form>
  );
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-13T10:45:16Z
author_association: NONE

PR comments:
user: login: vercel[bot]
created_at: 2025-05-13T09:58:53Z
body: [vc]: #O3kk68o1x6fkDilCaXnK6tNcFYwHaiDwtjPzZeyfEUk=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0My1nbHVjb3RyYWNrZXIiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vamptYmViYXMtcHJvamVjdHMvdDMtZ2x1Y290cmFja2VyL0U3NUIzOG9NdmN2cVlSREtaMTdlS2dEVWFteU0iLCJwcmV2aWV3VXJsIjoidDMtZ2x1Y290cmFja2VyLWdpdC1pbXBsZW1lbnQtZ2x1Y29zZS1jODE5MzItamptYmViYXMtcHJvamVjdHMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0My1nbHVjb3RyYWNrZXItZ2l0LWltcGxlbWVudC1nbHVjb3NlLWM4MTkzMi1qam1iZWJhcy1wcm9qZWN0cy52ZXJjZWwuYXBwIn0sInJvb3REaXJlY3RvcnkiOm51bGx9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **t3-glucotracker** | ✅ Ready ([Inspect](https://vercel.com/jjmbebas-projects/t3-glucotracker/E75B38oMvcvqYRDKZ17eKgDUamyM)) | [Visit Preview](https://t3-glucotracker-git-implement-glucose-c81932-jjmbebas-projects.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/t3-glucotracker-git-implement-glucose-c81932-jjmbebas-projects.vercel.app?via=pr-comment-feedback-link) | May 13, 2025 11:01am |


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-13T09:58:56Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces a comprehensive glucose log visualization feature. It adds new chart components using the Recharts library, a server API query for fetching glucose logs, and client-side components for filtering and displaying logs with responsive charts. Supporting utilities, UI components, and environment configuration are also included, alongside minor UI and code style adjustments.

## Changes

| File(s)                                                                                   | Change Summary |
|-------------------------------------------------------------------------------------------|---------------|
| package.json                                                                              | Added `recharts` as a new dependency. |
| src/env.js                                                                                | Added `NEXT_PUBLIC_APP_URL` to client schema and runtime environment. |
| src/lib/utils.ts                                                                          | Added `removeSearchParam` utility for URL query param removal. |
| src/trpc/react.tsx                                                                        | Added `GetGlucoseLogsOutput` type alias for glucose logs API output. |
| src/server/api/routers/glucose.ts                                                        | Added protected `getLogs` procedure to fetch glucose logs for authenticated users. |
| src/app/(home)/layout.tsx                                                                 | Removed extra space in the `Layout` component signature. |
| src/app/(home)/logs/glucose/page.tsx                                                      | Refactored page to async server component, fetching glucose logs and rendering new `GlucoseHistory` with hydration. |
| src/components/charts/glucose/glucose-distribution.tsx<br>src/components/charts/glucose/glucose-history.tsx<br>src/components/charts/glucose/glucose-pie.tsx | Added new chart components (`GlucoseDistributionChart`, `GlucoseHistoryChart`, `GlucosePieChart`) using Recharts for glucose data visualization. |
| src/components/ui/chart.tsx                                                               | Introduced chart container, context, theming, tooltip, and legend components to standardize chart UI. |
| src/components/logs/common/filter-labels.tsx<br>src/components/logs/common/filter-logs.tsx | Added filter label and filter UI components for glucose log filtering. |
| src/components/logs/history/glucose.tsx                                                   | Added `GlucoseHistory` client component to fetch, filter, and display glucose logs with charts and skeleton loading UI. |
| src/components/ui/badge.tsx                                                               | Introduced `Badge` component with variant styling for UI badges. |
| src/components/ui/dialog.tsx                                                              | Added dialog UI components based on Radix primitives for modals. |
| src/components/logs/forms/glucose.tsx                                                     | Made date picker button responsive, improved date handling, and updated submit button text. |
| src/components/auth/forms/sign-up.tsx                                                     | Added "remember me" checkbox field and enhanced error logging in sign-up form. |
| src/components/auth/forms/sign-in.tsx                                                     | Removed extraneous blank lines in JSX markup. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant GlucoseLogsPage (Server)
    participant API (TRPC)
    participant GlucoseHistory (Client)
    participant Charts

    User->>GlucoseLogsPage: Load /logs/glucose
    GlucoseLogsPage->>API: Prefetch getLogs
    API-->>GlucoseLogsPage: Return glucose logs data
    GlucoseLogsPage-->>GlucoseHistory: Render with timePeriod prop
    GlucoseHistory->>API: Fetch getLogs (if not hydrated)
    API-->>GlucoseHistory: Return glucose logs
    GlucoseHistory->>Charts: Pass filtered glucose logs
    Charts-->>User: Render history, distribution, and pie charts
```

## Possibly related PRs

- [jjmbeba/t3-glucotracker#6](https://github.com/jjmbeba/t3-glucotracker/pull/6): Introduced the initial glucose log page with tabs and a basic form, which this PR extends with charting and log visualization features.

## Poem

> 🐇  
> Three charts now dance on the glucose log stage,  
> With filters and badges, they’re all the rage.  
> Data fetched and displayed, with colors so bright,  
> History, distribution, and pie in plain sight.  
> Logs now are clearer, with visuals anew—  
> A hop, a skip, and a chart just for you!  
> 📊✨

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> - CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> 
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEejqANiS4BJZtytsMuSEQvYmiEpAv4iPNFIDADlsZgFKLgB2AwBVLwouISFwkgE0I31jcCgyenwAMxwCYjJlGnomVnYuXn5hUXEpGXkmJSpVdS0dLJMoOFRUTGLCUnIqCoVq5y4qAHdIRDDmNAp5OQV2lTVNbV0wQ2zTA240BgBrQJINIUR8DA4DACIXgwBiN8gAQRtS8eo6Itlqt5IVGLBMKREGZYN4lNw8mQGPILEgXPAsAADU4XK43O4YTGQOZoZDYbi0AH0DZoWi0DH+NCQcgLHGXUiQdhrSAFfB8TFPCiiCEUXCIJ5EubqWCQKQURDwe6QAUAPQATBoAIwAVg0AGYJRpIMF8PxcLC+AwIRgoZACJB4YiMAx4NIADSLBgUeDcMUevmQNi4WnUJlzSjeFZKDRGIwfb4WGgTRUYZD281w0QWVbUFPIMEkAAe3D5kwD3GwAlRDE5znUruhBigABESAiMEpnfJaUpaFwBUKrasxU8uE91VrdQaiRjlWy8bd7kTsB3KMrHavnQ3Mc9Xk3jogKAwAPRobjcY8AClgLBIAEpj9nZPg8BoxYXHi8nu9Pj8/uVASWVgQX4IohxtaQYW8cCOSFZh8GadAsCLXAqEWHFvAiXkhTtWEHWkVCPFwbAhXoTErXgCxaCFQkAioIM11nDNlQAGTQZ88CJKoS3IZwAHJkAKFcxBTRZ4CIDBqBI64YAtbxUAwU14PpAp4AYXMlXTPDfCINSPSYZwKHwCweV8OZ/T4SgjL4a1aFRG0AG5cOglgePYATIAiCEJEVPg4O0VNIGE61SFoGNY3jL5E3KPM7VNZilAYbNk3ufMiiLEtRUBctK2rWtxHESCm2NU0YOkOLnPwpKc3EVLQM5YtS2yvgKyrNT8vraQYy/TIDyPU9zyvG82AfHTEGPNwPHwLxj1OUg30QD9d2/SLfjGAD6CAlZuTBMrGzgbxMQAcXcTwSBYvxEAABSuLjXPudhiVJBQMDlSYCiM5h0EWWRnVgIzFOwQThNqySTO4h7nAq4ZSV+q0AZfZAEjlKY3Kh81qHQBgGDbMVlS8VYrRu+jECJZ6mV4fBuCNGwXEU1khQKEhcCtcrJrOnxLodUNZXgJlMTPeANHZ6brlIXALqIRANF4EgmZZ2BLzvIksL5bwaPaBkjQO1HIZcNASXUNM8MxAmj1gYm0GYUmeE+pBvHtFCqDEb7MXENgrsoRVaCJU56OZygPTmBArRQZBTkQLx6HJ22qeh5kSDmCxuzpQFjtO0WAAk0T5WQ7vsPXtbw2IbEWQixGkp6yQpKkKrmKhuEqgyaChjEFSUV3M9kaiAQAYVRdh87R3APTIFR7P8JLXWcMB2+8WBu5SjAi8zAo0GwRMsaaB2VCr8FIUBD6WEgPjyV8Wk+IqviEEQAg1j4lfbZIHzEZ4bMcZvKi1yebPb9zp5ICwlpIxVM8AO7MRvnfeQwYBB7yFA4M4gINjMTOMRNAJl05TS8L/KBQ89ZBxDjKQcJBJDlWYm7eAHsvaKCJLfb0Np+B8GGCQewuB5B0IZDydBFh0gXEfmffAtI7S72bhibAZDTQazXOQk6WCSAADE+TMDwbxFwwUD5hVjD+BMSYNKBS0pmaqS80oNUymWFquV2rsE6o2KAmCzqS2urdXWqixISSkjhMq9Aj5fUxErSAABePQkAABSABlAAGhoAAoo4QeFUBaIDhpAS8lNuCIC4AAb0WCQQmFsczWy4FdO2XhgDZIANrnBILILgHCbQAF1amoU4QAHzLvQog5T6mQDaSuJQqlyD0AAL7BKGXeQJwTiksHtsAcJUTYksPYHoGcWJDwnjPBea8t5RqXQmhnGac1rjvh3D1fcYAjBrOPBDVR40hyinGiLGajySBgHpBwgQeAUwLSWj1Va/4JiAWBDtMCIUipfATgsAASrkl2QlnSg3QS4x69jRbNjRN6D5oNe4ilwDOZAGJUKKA8ICe0UimGemoEmfeopeZLHQfAAAXpw55Dp0XwExaJBC0jKEyVpljHGvo0xzFNGkjJypnmOMxB6GGFAqCgiKCytgpJpJOBcIOPktBEDSo7MqO5uBe73FUkQKVL0jUkT0Tk3A4gbSCQDMxPVL1gwYkoI/a5j0gZkJNmE9SVrKDYuHESHxlVMSDhxTbVEAgqDcnrhswEbcwHeCZAwIGBBfH+tFAa5wAVKAqPYK6nFKBnTuCUMgJkvdhzSD5lgIg3p6BSnNLKSg4h1ImXsuVN5486DasgBEsAaBCyoGwisX19B7RvIQfISkNB8VYCeM2ZskAACyy6AFDsxkDThmJKSyFuCazA0dIAAE0+0DuQPAoUXg6wMJZRIdB4jpbfDisZcQjchipzrdKRgKbj7N0ehjFwCAS2VWzBECw2rR14UQD6qlDh8AuBLAS0tOFb6yCsB+htTJJLESoODEiUgkIHsQAiF0qkayqQsODYyfJH7WM8QW1ASJfBR2JNKWcTIUNocYKsegJckVQ3rTKJkQD2h2ksICJ4siOZvOaRy+4AD93fRLV6H0oNFg3ilAwhKAJIBUAgvVZiLKxqMO+mvCjvDziBmkIgK4KAiiKU5lLdAOFZaXs0JAGwdnTSGa5qsRNt7KKdulYwdgEZ6BsEjjZp4JpXD7O8EZ3kfSAGoHHU+ONqYaBCLBPanFrr7quNQNtKp0dS2MAHlDN1/HP3kNPl4Ur09cB8SJPSQc29wpxl/NFYxFUEpZhqrFAsjUsr5AsW1Gs1jCq2ONInUxTV6DQtQXxlwKKvBoveZ8+46bcVVWSoCUkjxIDKkxG7RaBgDsZVLC9W+kBJOorZbJjAm3AkpOyRKy6+kcWZqNZAIZWSYtyMcVwI6zMbteEcQAeTwBWEe1L9WGvElwTbn3xLffGUE0JkSYlxOcKdw7Jy9wQHOQYS5FWxRXLDXsuRFOzpgEgbnb5n5Xh/PWgCzaQL5X7wgo2cFLJIDzdhSDFMiKSfKhByQHBudNt4sLYS2gxLR2SMROSwzz8SAmVp9yPVNN9bY1xkKkVRl0n9le1LPdWBVhyvqoq3JSw4KPSnWgcDuqPtw+NUFgyZql71BELCu1eEHXN2zRQXLBdXEeuNodUNw5w3sqjdA0qQptNMjbTDwtc9voccBOpCgPGbD5uHKyojT5kA3rveVLlfB3YkCDlVvChYT2DqUZSklppEDqe5tOwtkA50LuXYu1dje1EKgYVu9iu6880oxElbAQGy0VoVMMGtYCWMNrempRFbbkAdqrF276ya/7MEZc359Ppl83jwN9EDqvHfsfgnBmUWHzWtudTptsF7rHXtiz4FXFhkACZfC4WgODfMLAe3HgfARDI0RdVYXSQKXzS1fgLAZiKwAofWHVb0IgWAFwLLP3AtbCdCM4LWWSaCbjQBXJETVvfAOYcPUTXAKwLvUXL/KQH/eTHVJkJTb0X0USDEekH1ThLTGgHTA+fTPCbzRzAMJkcLazDkeATzBzJDbwBLDsGmWQ0Q+Q9AfzEDKwN3ELUiSzCLDkKLLzT/eLF8DsJLTfJACdNLW+Mg4Q6CHLLRSKTrPRNMeKPCRKZKFw+qc7IbYzVqPKcbBsTIKbBYHwyYPnFwEnfsUXcXNYSXU/NxB/HCPxF7WLAHa7YHNIy6CHXAKHd7YcJHIgBHZ3DAL7MZCZdHeZLHLbWcU2fqEnW5cnZ5KnUWGnHONYb5PHb8M5C5eovLdgRoqPFop5WLMAbgV0enZabRP8ZnWuLaECXaUFLnCFXnGFSI/oqGZbEgK6V0eIwYQyIlHGeXWlbAelBlbwZXJggvGTdbQKDdBhCmV0GHLXflXXcVLIk3dAErNJAhNSGUAPNuP7DmJVG3RZKGe3YPYeWOHGSOT1ZLHmDYJgFca1fwNvFYDAeQEvdwcqOAlQLlSATUAAFkJJSQQAwPGQU1A0oMgCiDVBSTMjvBXlqyROcDUIzCwDXRHWl1NDYJ5nN3Yh5F928HGPsOHHH3RDPUV2sITXTzYU4yzxzwSKEzILXAU3YJUy+SIPwigw4NUwoKoMqinXViEOwIuOMK5k5MmGeieF70XSPUPQdIAXuP8BHx3VJgshM24XMz0KkPklkKMyLDREfhFJTzD2DUj3uSJAjVjwI0LSnxnyfUTBPwEy/X30P0qHuBbjcx1n9yo3lFjMvx/ycxclKPEmknoB8iZH5j1UKKJEEG92zL9w2PWI7HrHuG4XkDJWoJDIdRkPbyZFQBc0enEJ9Jsz7Ps1UOLMFL6UhL1jDh5A3mTjq3YFnhlIUzDK8DlERV4waMFL4CfH/1jI4y1nax0RijqgMW2z6zqgGzMWah4EsTGzrAm2COCGmwiMWxF1ix2JIHiLCN203zlnXk3iDTqJPAaLJyGOaOeTGImOOQSNFTtFkARGVFSP+0ukB0yPQqlhyKh2+0xG6nxz6nAubPGjGiuRYHggwGPHIyTDAELOlnfAZxWl/DWjKBZyBGAmBQ5yhCMG53fLWIXPhUF0oxD0ekkjYDInkUoiTDYlAxtn2JlzlxeLOAFTxlwGFVjkN2VEr09m9EUDktVyJEvHY2aRtApJ1WxGoFgAkpIGMtMo6UZK1OFy7N30zMD1ZQkASIKCsELB8HYkPIUyIDPDLlQ0IJsFASUA9HUCCi8BK0xAACFaRSBc1KsMMsB/9k9b1vRMBocY1zxOE2DJBcJMYbxvQGV3KKNuxUQJJqDCy7QUJYyk1qxLht9StRYUADJXUmN5IDJ5yyVARnTg0IkbADJUr0RIrE1WIMRzhxqeRPoptCxNBbhfjQ5JIfJgqO9Lzk1ZVHpThzRbKEjmInhdLqFaAAEzZQ4/YrYA4/IWEuUwpIBYhxBUQ2EhKRI6pyJaIFMQ17qpAwlclzZLZlEpyPVvEAxqpI5mQbrdYFRVMFNYhIUWJAxMAfQN49EPQL1iNt5k5uqOq1IlR7ILNUAM90MZRMUCAsBsqq08YFMb9uExJzjStSQH0czmz5z/zitDSgKN4XAwi2tpjnDQZXDuajEvDbzZs/DHyOoXzio3yFh+lgK+bBtwjBK4UPqwZPzMRpLOtDKf8mtetdDajidSLHxdkqgqKaKZLKB6KVBVdGLFpJRP1ELMRslTr9LaA9amkOkPR9qbKbrvbOEhkCKpieiic+ixLWSzapYKLWB7grbOt6LLpJjflWL/k5i2d6o9o+KViPz1aEVRKoTbKpLraKBJV5yCUjJZcjjVqiFFcSteNVcwSXA8DaKvYP85FGDVc5DPJoEeUeAzrZzXFVK3j2MGQ6C0kdL8Bj9uBM0szjK6kiBxkwavim5MzhyigmQCAZ7H4dzUo0QTF+YwlYRmY5r/0t5S0StEBUQlAwBy8AhyALBH5mkiBSA+A8DzQSaT6JTvoKalQUymQ26+ACasB8qER6B40O4t7p6kzG5z6Ut2JuyjIfJewp6Z6574klQbw5QaZJrKpW8SBmZHV2APQMwcIhhSDgE+AAGaC6CngdaqVLj7aWCD11TOClQvANbHUApCr/LQNAQa0XxG5sChQ4sZrWbYRasibS0KNqSPV8z7QgGe6NgngoowMAEng2IrtwwSBzg3QAEAxNHSQXAqLzQAFK8B6PatVOQzgZRSbV7MrnVNoVgKNPI8BKakZTgMAMBOEhJXGpRaBzQh73U4qgoXr1B5B87YoFNdy8DjyHidUDy8Boqi1p9N0GBaI8DIbkBDqDIfUx5QYPRMQ/6MAAA1VYGmm2PAkp0KieR3XjXgShesRCVEKpZUY+wh3FIp6AGBl9U3MiFiGawNBat8pa/EYJ1uZATm1enrNeXmmbGlU0wMIlKwAWpw3RYW7rdww28W9KFW+8/wqxZ8oIuW6bRW+Z6ZvOgXdswuuc7W0u8ulMhUdxbDQ6S8N23pn0DBqGH7SAD59B9emYdpIO1HYJOZTHZulZfGCO4eMi82yi+OoBpOqWTo0OgnXokiyO0nciodB5WLFOxnNO2YyYeY7i7OgwHMpYl/eCfDQB+AQsQEAJhtDPFJJ4ZgPsOYMAcpNUAADgAAZuBCx6knhxkg1+DhS1Iqk+ASmPQVhzhOEYq/GlymXYAwAsb96pBH5zAPZJXKAPJTZmZmwAQiRbIrBgHq4jTjiMpqxLBWhYQLg9zPSf9J1E80DmYSJkJVhk5fahQpAr1J5uFf9P0+k5YnHjMMBFyUmbDMsihzmkzNM3DoISIaIACARH4lgBAD8XAan6qaAlr5yvF5rj4ngwlKws3zHTQnhYhYMhEdIAFg4yBmQ4NOZaRCCDpatlIZC19NnaQhAU0nrS54miBor7BkG4RtNTXOEjIPlb5yBI5HdyQjS3GrVNJGq/9z90EkxOEmAcIh0HN2oAxb0b69E1mOsNnYpLyPDrzAoJbfCcpRsZaTmoBos9otnDFPDNmAxnmkiy89m7zhsHyH3AjypjaYW9Y4WY7cXhijlHbCLuj0Xw7MXYXo7xp1dZBoOCWWLvg2L/gSXM7FiD5liecPyp5HphctjYi84K6Djq7m8C8J0gSOqdJ+zvLS7CqdUfI6VUQGUT33MW7mZWZi9zTHNYY/oEYgYlyhqmRoBIUrpe5IAABHcRbkG8fACzBTdEq4QSUuwCYMAQ9IZjJUfmd272X2A3D0JYc8UsThJ4bMW+AAdUIfOCeA9Fs+McXUzNgBc+MwZohOcvZrDNdN3QqiUaMw2CXdUo1XHtkCNHs4QDoPPnpBtGirVXrvTyqSsEprfkQU/hE3I5iPaNkDCQy+ZiXBKpMaaYYDqoCvPyWb1VLR1SAaDeZZK6y8buqOliMDB2dHHeDHnMS533tXZoMnpALqXNctC7tqLLpsY6kb8B5FLoHZeIQdkGNlEZhyRgTTADkFXI7jYxfyIw1YuNrS4CE0K5hyC2kwxVuIu6ap4CeL1VHlsZfxxkkD4LwiAYEeE4a8IxFEzwLXdzLM93rMaHQlEBkNkE4XqpibzMfiSdQM2hxEKspF9EdZUiZhTc9FEawAVHOIfWesogifes2dNZLNG5EuZqhtsttX3Jq/1hqowFVU8lJGykQLwiHJ6/qi3v7oRA9r4dV3Ch1kQFa6VGF0phQZxMuzREekS84V4xTIQQ/mMnaBp8qiUebiMmm9YNcfNHW/q9lYCidR8c00gxhsX02nLleaPKR5tAmfp7uEAX3SsCmdlS/b0/Kg2H1N4YIGMabfEFIwtQUx0hgP8GYisjtUTcu2Ml5RUO+6nLQA0MCxQAAMsKL2+kplYVrCRKoBD9isoBFtvnz1QjOHlY7o5isCYIke8Fo0jBWfkiRlVwKDAABMGRHbMU4XIBhOsw1wB9Io9E+VeobHqdLibtVWsYUzCOoIo8K7muejjc0FPKinPYvMj6va61vfMUA4COOaKmfbOZ5s3kucEvy9i0o4NqMTS2haQ/A5Q+PDQ4w/goV4N37Ddp5T0u9kDoYWDpjD34WAJUoE1okd2a0/P+GsGK5N1Ka5/HbBA1WRgcbkt/e/s8k6IpJFIytf9oyTRbEVY6yHbAPAGPDpBaA80JilMSZzsUM6XFdnOS34pQpBKe9LFtDUkrKgkqhAuytRyUo11tS4kQZBVDJSylUMSCZKtX2qIJE98qaRlJ2llAVNcqVfT8mGXITZMwA1NTADjD7R4AyqETYygwFvTjJoyIEMdKG3IDp4iGYIXuGEjCRhNCeb1bJuVBTJKCoYlnMxI6yHaPximgg8pjlVZJ3R0suVfCAMnKgZ5VeCWPgHYLFD9h5+JqA1iNxBARCS0lvUhP02VCONyAJqGFIJnPDJxOE6PCMFDHMyCM+kHoAQBqkDgNV82TAXwPKEdzYNKANFfAHvjLgAgZBiVQQbPx1yCp6hHYEgkRmYTCDRUHoEIVpUEgLVXBLA9wZUwSHDAqYBdZUKSGxSUQfYnkGBrkiwBpJYusILEDMPi7zDUAhEKvGvXoGuUj6vgLbKBQAACVAekHXlwHHgE8YgWeEcKJD2huE1JMlIVRKxZDMeo/R6MHEojCk38FAHyAwgCFOQ78lAKUF4BS4v5Vw+ZfmF0MJB29LMFAW0CEO24s9KgdnUJgAwxLoA6QbZTWuRHRHBAbqvsFmoCErIDDYqvDAfkTyiZKgvqBFfzvQPQQO8vAtNdcKGDuFwYAkTwAgaQAlDoArU13AQppDwgDV6Anw5wEaGkpgxvWCwhtOQmYEpVPyCmchDyJICjDpBtCOUtuxdzmp4aOEaZngRQgADEUHqU9meS6yXsdmn7P9pLXvbb8CoT7b4O+gWaq0Fs5HBUawKf5UwxU+IlmoSLYAAB+T/q7mVAhCgxXeefgAjaRPBOG9wSkGsCjERiCIFAIiKQkTFPAkhJACUEUw2FzDwxhQ6PpgBNQOAgY6AVxoXw6HZ58YXjIkOKPgwG5rGoHa/vAKuGqjOiP/J0ag2mYGQKxS2VUeqM8H6ZkKh0LQWgCJD9DAeRAXUaJGqaCDamCrWAc2IGLHhWxgg9sZgMJwm0sW40K4ZrmIGp1sO6dPDhQKzpLEc6xHY/r33oHKQN49faXFXTlwFDcBm8TSHHCWbQp6uPgGPCCBeLi8E08VRHO5WdQUAz6EIFwPlWQAfiw0l8TENCkO6gIpAc9QPGTB1SadbQDqDMAfgSYZlSyU4z3GSKAHOAUILgwCbhIeHDiyxZkTxuDwKCQ8GEZQgMEKTYAojmMpg8wUoO3yq8HUdY6xg4Os4MIaqmBWMvGIsyYSuoWpTEJtjCRylWBwuWgL9Cthr4qqhaBssgDYmplU0WlJtA2GZ6Gc2evwhCAmhwlfZlRsINgBZyYDgM32KeNuMGB66C88IN4ugpPmLTlQyA1oI4omRfQFkSAYwDMtuP7CbYemALIic4BQlkRNsLEXyXkG+a4omSLkbcZdjCAOwcC+eX9Pm1JSK4NJ0yc4vQAYrRUexjuBicA1bI+o+QMgVEQgTXq4TYypwVDIInoB+cdY29WBkCCs73J+A7DfRKaEAxxYpu+YEqTwWoDlT9I36A/EzWYjcFu2bvfgSkkALQ420HoSkAQ1oB3hHcnJfnlrwrKl4ZBVgPyWJAPzJRxuaXEBgNIUDlDFgJIBWDYM/T5SDu2NThJx3ZSWDIm2YFFjnVhAWBeexPUSJiHFg3R6ptIQovIk+gAzEutYpas7DxiTjpxSoJQE6iLJ4EmQukP1ixzqmJdk+LCTGtIGMgAj/AVSVbkWy+hztJg6Mhqe0iIjSQRaw6UOENxqlcgGwdvMkLVk/rUyQqoBAmSLRhm6E7pxU8fjqlOnhQwcW5CjKQ0cl18s+Z4JYNmA7wYSzJDICzlqOS6xl3JygwEO12bqq8WpL6H7l/j8nWM/xSXRkFLxsJQwYmY08QW1T1ScIMoXsJEN4BTJQSo8ZopfueW6mi0P2/WG0XexGz2ibEwRL4M6OmZsIUKkkkokagNoDJt29GGgF9G5kWoUykwinoWQKn3AipeZYzGJI7GByux+zObMf3Xr5sw5BRQuVtgUyqcLMmID1JtiViBpxCOuSONHLSku5s5QcvOVrVIlG8c0xIBuOMQeKeVYyTsjqbBOkA8QFQiEoCeMGLHIMwETcmlJOOIb5sFMUGKmLpzCq29ioOcwEN2OAFSSZJKyBspwnUkcTnejrXMuUIql6TTUyOUyYslbm5z/2HcnFMFNgaxSiQLw/uaIJykSDtZJ+XjENVzI1SFMQ89Sp80biip75289ueR0inRSOwb8yEZrE/kWyeObVPaXkAHYUj6JvfIBTqhAXIB0FOqCBZvLbmPzPp30mkViH+nsREuwM0GTQoamXhJxxREubhN9oMLaQXAFcOcHpgYAPQBM4McrBfzYZjek8F3I62RmkJlhHCiBrHMgAEyOx0KMANM2LmigX5fTWMqoolhwL5hQafBavT9hYEigzEJydXxVqddTkCHLcTgLwH0h0EfgTDtMRw4bROK20SgWeIMDUDViC2Oxcx2FymK2BD4jgR8hkpVSCAwjIoJClbZ+US4HkXxfN0aZZtSED6PlJPyMFGLFsxsTGPlV5zRLWU9i/wIkuaY3SMMOIqYUOyPKW9K4WTfeqbJcC8YvICfXyI/BWC1E0UBSuahBOVDtKdIxSJpq1khTT04p3SvmDpGgDoE36EQnpX4Cuilh0ECQzENMqID9xRYtCJpkdPkA5KMw8AO6kRkaCkJclFw22P0uSUehsIJIbPIVVcaIU6aOIhhKAUpSCjyocTI4SgE7D+9u2XyIwIstGV+BhZlAJ8GTDpBX0WE8AMAMX1TB+xHoVYEvszwuB5CdU5eJ8N5WARNUfGw6USIX1lmZlNenGcLnyQFHso8AOJHVFSKsHojxJPygpQgohihMEovy/wHQkpk4QNgH8l0kstmWih5lRTJZf8ooCArHcdMrMsWTDCfpUaGK1KL7Wmi4iFZlS9ef4DwLKREUsaHMPZL45xlXJJWJKB1RqaYjOqRnVgg3IVBqBXqKIKbuFBzm4j18dPTJSMoKWZwVSIEzRUssURwZu5hshQr5XZR0EW+a4ekBIGa52MlZCqgMMJlVKNchla4ThsLSNBUqxlYmcKXap0itgdSGpMrjkqiVHKil28QNUhRLA1ozwC8Wps8oDDxLUSZBBkLrLYYxqEwtzG5K8VR7ZN/RiaRFWQwGGytKAufAIbzCrIWDTVP0pUDUtbJTDsmDArqFBGWay46CaShmhmGZKkVHWHqTvCEqoiFUjV3q3YfYzLW5qex0vewVUpwiUkSA3kZpY4TPZuyRaPWMWtaJdEHNpawHSbFvLFHtygBAUpNX4CmUMrxl4kSZTyoZWcrgwFgT9dSp6rAadIfKgVe+uWWAsumUGh1VQzA1+BXVSYRDUQHMC0E7Kf6gpSmuUxdSoWYFbATfyuFlr1xlirAWQAkD4hmKTio8YChPEEdOcE6ijTsvuBM945qmEkBa1rj2gXJ0+KajzlI4zw08zGgGEzxPmHRgg0SCJNAAAD6V0WIAlRYg2Be4Mmr4FdCugybEaLEekQMGQDia94PYOjvajKwuAoMZk0VRhiepI1gWDCI9mAkD7a8HevG1BkxBFHIl+6Im1jY9HE0tAUaBVBhIqyGGUwu+GgCjRoEk3Sa5NCmpTSprU0aatN9I6LCCP3J+AD2lkV3jZCd6cJONPFOgC7KFoXtV+Vor2beoA6HMnyDo3fp2LTgRbZN8mxTcptU3qbNNSNLgAyg0CL0lYGgEiBYFrnWT8R9WWhKzBWCd5yEFGiGY4KbHHgwtY+Ehag0xC1aotDW2Lc1q021AjIIWsLYtvq0xamt8WpGg8Mj4hp3NbAaJK9DrINAXYrmw6ONpdGd4CNM20mHB16ibj+oEaFcS9QdrUbSBuHOjW4tPGEdzx02MlZE2uZYg4IXKAGnkmBqShyY76F4sGCqQN1rNftQ6jNyU6UB5A11BiHwDR0zooc1jHsZbxKxKsrNyNDYLLHbBGykItYHyKJu81SC2qBnbwFpuxk0tPU3gPZSRldD0AMd3IbHbdSJmVQtNHkS6jKAF1Jh+Z9AIUCIuoKLta4qOmGlUDUDcCUyJixQF20AiA1Q4i9IuIOjB21hO0kpGloijBBj0bQdBPnVjoKSC6g0ki1GVpoCANpvhE9P4XjMqgXoMlzER3UyorhCh8ty/d2Ves9k3lvZm/crY+yq3yIDdhmvsMqDCIDqsAEO/6trvyT0RUk1lWysGPYX0RBF+Gy5O9oH4O0uiL2jFseGRjVDBY1w//Pnwf4ZISBRLMgceP+0MbeKFLdwjISKCV1DikvHnJTBoBiBAQVu6EnQErjF1xUzMcupeT+mxZBlxKkCXrvDgbbR9u7ATpIyChwNTQmoPlny1m5xZ5uDMktRXjwjrx2SzaWuPIyF38EVALPCzk3REgm9DoYCCIc8gWVGlA0roKiIMOPjkJjcDwztI/AvS801Cn3akNAj9zJsQmerZADYGbAWR2gSCF1jQEdwy6PWeWrUsFpX3yRUmQGcPplo7ATxyuwi/6JQSmanoUSyoGTnJ2iQZaVkFYmErGS6XJbOQGWmdI8JWKYgqDvcGg9ZCdrpUu8NgYINAGiSQpggXwFiDJrCQiHSmIhmTSIchRg5IUACNoC2ujhm5zwBuHKgIUkJ4hOxVqqqm3wuxYrNZoNGuJMB404HDoXBng3yETVjaFOgaA3aBWoiMoGUVgO+hQGUThR1mF66yWv12alapaQHHfo+udGYZps/exoEPuU5Y7l9suZIuLCn1Hbnkc+lDfdsuQV6KAA0PAUZHn14s5EC0PgzKGYjfsPE1gA7Djj+mT6MKscAfRUCmRHFpIGgK3SZSSTOhnsjAXAB+D+YOgBA0qNQd9hRwVEpkB+UpF8FlTsQykYCD0M8iWkAgRkegZWBuLL2oRuAJ4G4ZoH3GEtDxxLP7QsRBSA729opPTD2BKw85g5FE+lM9GOhYUHE2RSHJxGsmmLa685fpGG2ejMR/8eFEOd4CWZVGJYl0MzrUKwNBQoRwaV/Tphr0lTKoGIDHroS4OQn59/AB42KEX3YjyeHDckA4BMNh9yDm6VI5QFwp4AbYPxssXzEDWHlO2tE9jvIEDK3woeqWmsBDRxXGRTIlBR+PcCXLMGqTnypUBQywkBhLSyPPtrfCZ7Xb0czYWoVMCZ54FYJUJok2KBNQwR3u2B95RaiWbGMvYwvL8XO2XZ+90ABQJmA/oVVg76UbCbw+eotFFbr1JW6ZnaKOaVbQjD8yWqSauM+igcuAUXODhRNPYCTFABU4gHKR8RnkfEepEGcSOXRQz+e/qKsfWNrFUWZG17UuKjqn7YANQzw+NG/ZgAMQjin7S4tJbuLDjlLQjtS3xJOxMAJAV+DCowAWYN8BpqlGH1aUmQMQeFVSKrmpC+AHWdNFAtInZ4s1hUVYls+fjbNURPInZizAJnFNZnZwe7EnEaGiw6Q9IjqTXmyfMjGY8DjvAgwqxkZJh0DOsPdr7qt4KY2V1LAKKCa8TV4G0nJ+QAKeoYIBp0GEUrEsPJCp4ZSzEOZCjQoDnByQAe3w5aJtOh7Aj9pirf7JCAPRljiHQjfANTPpnrY5ergWAB/NbGsOMxJvXsbJYeKheCFl87OfZo5aeTdHHsN9B5wFirAwwEc/QEFCLJUgFARdFmOslDo4lB/FwEe3vSVK02j6Hduq1bLYLRA5wQoX5SHPgTbpU3LvNCjYA0XLM9bZ6N3to4wC1eSiAdgUNMLHFWZKxCi6Lvd6xkyom5uyC6i1KUJHBYIKuV4ABqNz01z0ZPQBWT7zl7MvgCCHwDBqCqTY2KPiwJc6UyWC4vhe0HxJpTMQBN9rfi/gD8oUWjQlqsbrIDdypRo+DmeobHK+Fw7UG9oL4+fntSQGoYe7OzVOlEh4HgCnIOUJ2UVyPwDzZbJAAqEJqMmVZGW3S4Qf8izh1EEETRBFEtNeF/zIem9mHrvXBHHTr5UqFS3DA4QowDsSPqUdeYmI7Tvsh0zYlG03wFuqzTAYcD6C1h8gm9PAKMDQv+TVUswA2K4pAiIlFAygToLsB6AHADAS1i2uoBk1gJEAMm31q6HDC0AZNhfGlFkDOs5BIAfLbUGqCiB6haADAQkmqBUAA3tQAANhBuEkAAnCoFoAFBt92oBgDywYBg2CgENqIDqEJJoAQbaN/YIYCWvb6sbfLP6wUD1BqhCS2oPljqD1ACAIbDATULQAhsQ2eWMNnloSQECEkGAENjGyTd5aag6SvQd65qBUAU2CgGNhgAUDZsCBtQJAHlije1A/WogaACG9qE1BRASAmoHlgID1AkA9QNN2gJ9aZD82IAkAbUMrb5Zqg+WAgZGzre31RAIbhptUNqD1A8s9QhJJmzyyxuM2eWUQS28rYYB8sDbp1pa1EFoB6g+WDAKIOpGDuQ3CSJAEG+rYEC23VAJAVm2TbQAFAogctgoCDe1Brw0AJAXfa9fOuUVLr11268/Hut0AZNGC/QEAA=== -->

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

<details open="true">
<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=jjmbeba/t3-glucotracker&utm_content=7):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `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 explain this code block.`
  -	`@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 explain its main purpose.`
  - `@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.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

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 generate sequence diagram` to generate a sequence diagram of the changes in 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