Repository: jjmbeba/t3-glucotracker
Pull request #6: Implement glucose log page
Author: login: jjmbeba
Created: 2025-05-11T20:36:49Z | Merged: 2025-05-11T21:36:42Z



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

- **New Features**
  - Introduced a Glucose Logs page with tabs for history and uploading glucose entries.
  - Added a validated glucose log form with fields for value, type, units, date, and notes, including success and error notifications.
  - Added calendar and time picker components for intuitive date and time selection.
  - Added customizable UI components: popover, select dropdown, scroll area, and textarea.
  - Enabled protected API endpoints and database support for glucose logs linked to user accounts.

- **Improvements**
  - Updated sidebar navigation links for logs to simplify URLs.
  - User button now shows a loading skeleton while session data is being fetched.

- **Chores**
  - Added new UI and date/time libraries to enhance component capabilities.

- **Bug Fixes**
  - Removed unused variables and debugging logs for cleaner code and better performance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/package.json b/package.json
index 078cf11..ec063f0 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,9 @@
 		"@radix-ui/react-checkbox": "^1.3.1",
 		"@radix-ui/react-dialog": "^1.1.13",
 		"@radix-ui/react-label": "^2.1.6",
+		"@radix-ui/react-popover": "^1.1.13",
+		"@radix-ui/react-scroll-area": "^1.2.8",
+		"@radix-ui/react-select": "^2.2.4",
 		"@radix-ui/react-separator": "^1.1.6",
 		"@radix-ui/react-slot": "^1.2.2",
 		"@radix-ui/react-tabs": "^1.1.11",
@@ -36,12 +39,15 @@
 		"better-auth": "^1.2.7",
 		"class-variance-authority": "^0.7.1",
 		"clsx": "^2.1.1",
+		"date-fns": "^4.1.0",
+		"dayjs": "^1.11.13",
 		"drizzle-orm": "^0.41.0",
 		"lucide-react": "^0.508.0",
 		"next": "^15.2.3",
 		"next-themes": "^0.4.6",
 		"postgres": "^3.4.4",
 		"react": "^19.0.0",
+		"react-day-picker": "8.10.1",
 		"react-dom": "^19.0.0",
 		"server-only": "^0.0.1",
 		"sonner": "^2.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b81b345..31c25c5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,15 @@ importers:
       '@radix-ui/react-label':
         specifier: ^2.1.6
         version: 2.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-popover':
+        specifier: ^1.1.13
+        version: 1.1.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-scroll-area':
+        specifier: ^1.2.8
+        version: 1.2.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-select':
+        specifier: ^2.2.4
+        version: 2.2.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
       '@radix-ui/react-separator':
         specifier: ^1.1.6
         version: 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -62,6 +71,12 @@ importers:
       clsx:
         specifier: ^2.1.1
         version: 2.1.1
+      date-fns:
+        specifier: ^4.1.0
+        version: 4.1.0
+      dayjs:
+        specifier: ^1.11.13
+        version: 1.11.13
       drizzle-orm:
         specifier: ^0.41.0
         version: 0.41.0(gel@2.1.0)(kysely@0.27.6)(postgres@3.4.5)
@@ -80,6 +95,9 @@ importers:
       react:
         specifier: ^19.0.0
         version: 19.1.0
+      react-day-picker:
+        specifier: 8.10.1
+        version: 8.10.1(date-fns@4.1.0)(react@19.1.0)
       react-dom:
         specifier: ^19.0.0
         version: 19.1.0(react@19.1.0)
@@ -724,6 +742,9 @@ packages:
   '@petamoriken/float16@3.9.2':
     resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==}
 
+  '@radix-ui/number@1.1.1':
+    resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
+
   '@radix-ui/primitive@1.1.2':
     resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
 
@@ -876,6 +897,19 @@ packages:
       '@types/react-dom':
         optional: true
 
+  '@radix-ui/react-popover@1.1.13':
+    resolution: {integrity: sha512-84uqQV3omKDR076izYgcha6gdpN8m3z6w/AeJ83MSBJYVG/AbOHdLjAgsPZkeC/kt+k64moXFCnio8BbqXszlw==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
   '@radix-ui/react-popper@1.2.6':
     resolution: {integrity: sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg==}
     peerDependencies:
@@ -941,6 +975,32 @@ packages:
       '@types/react-dom':
         optional: true
 
+  '@radix-ui/react-scroll-area@1.2.8':
+    resolution: {integrity: sha512-K5h1RkYA6M0Sn61BV5LQs686zqBsSC0sGzL4/Gw4mNnjzrQcGSc6YXfC6CRFNaGydSdv5+M8cb0eNsOGo0OXtQ==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
+  '@radix-ui/react-select@2.2.4':
+    resolution: {integrity: sha512-/OOm58Gil4Ev5zT8LyVzqfBcij4dTHYdeyuF5lMHZ2bIp0Lk9oETocYiJ5QC0dHekEQnK6L/FNJCceeb4AkZ6Q==}
+    peerDependencies:
+      '@types/react': '*'
+      '@types/react-dom': '*'
+      react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+      react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
   '@radix-ui/react-separator@1.1.6':
     resolution: {integrity: sha512-Izof3lPpbCfTM7WDta+LRkz31jem890VjEvpVRoWQNKpDUMMVffuyq854XPGP1KYGWWmjmYvHvPFeocWhFCy1w==}
     peerDependencies:
@@ -1356,6 +1416,12 @@ packages:
   csstype@3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
+  date-fns@4.1.0:
+    resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
+
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
   debug@4.4.0:
     resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
     engines: {node: '>=6.0'}
@@ -1695,6 +1761,12 @@ packages:
     resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
     engines: {node: '>=6.0.0'}
 
+  react-day-picker@8.10.1:
+    resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
+    peerDependencies:
+      date-fns: ^2.28.0 || ^3.0.0
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
   react-dom@19.1.0:
     resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
     peerDependencies:
@@ -2274,6 +2346,8 @@ snapshots:
 
   '@petamoriken/float16@3.9.2': {}
 
+  '@radix-ui/number@1.1.1': {}
+
   '@radix-ui/primitive@1.1.2': {}
 
   '@radix-ui/react-arrow@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
@@ -2412,6 +2486,29 @@ snapshots:
       '@types/react': 19.1.3
       '@types/react-dom': 19.1.3(@types/react@19.1.3)
 
+  '@radix-ui/react-popover@1.1.13(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+    dependencies:
+      '@radix-ui/primitive': 1.1.2
+      '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0)
+      aria-hidden: 1.2.4
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+      react-remove-scroll: 2.6.3(@types/react@19.1.3)(react@19.1.0)
+    optionalDependencies:
+      '@types/react': 19.1.3
+      '@types/react-dom': 19.1.3(@types/react@19.1.3)
+
   '@radix-ui/react-popper@1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
     dependencies:
       '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -2476,6 +2573,52 @@ snapshots:
       '@types/react': 19.1.3
       '@types/react-dom': 19.1.3(@types/react@19.1.3)
 
+  '@radix-ui/react-scroll-area@1.2.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+    dependencies:
+      '@radix-ui/number': 1.1.1
+      '@radix-ui/primitive': 1.1.2
+      '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-direction': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+    optionalDependencies:
+      '@types/react': 19.1.3
+      '@types/react-dom': 19.1.3(@types/react@19.1.3)
+
+  '@radix-ui/react-select@2.2.4(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+    dependencies:
+      '@radix-ui/number': 1.1.1
+      '@radix-ui/primitive': 1.1.2
+      '@radix-ui/react-collection': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-context': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-direction': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-dismissable-layer': 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-focus-scope': 1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-id': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-popper': 1.2.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-portal': 1.1.8(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      '@radix-ui/react-slot': 1.2.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.3)(react@19.1.0)
+      '@radix-ui/react-visually-hidden': 1.2.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+      aria-hidden: 1.2.4
+      react: 19.1.0
+      react-dom: 19.1.0(react@19.1.0)
+      react-remove-scroll: 2.6.3(@types/react@19.1.3)(react@19.1.0)
+    optionalDependencies:
+      '@types/react': 19.1.3
+      '@types/react-dom': 19.1.3(@types/react@19.1.3)
+
   '@radix-ui/react-separator@1.1.6(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
     dependencies:
       '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -2851,6 +2994,10 @@ snapshots:
 
   csstype@3.1.3: {}
 
+  date-fns@4.1.0: {}
+
+  dayjs@1.11.13: {}
+
   debug@4.4.0:
     dependencies:
       ms: 2.1.3
@@ -3125,6 +3272,11 @@ snapshots:
 
   pvutils@1.1.3: {}
 
+  react-day-picker@8.10.1(date-fns@4.1.0)(react@19.1.0):
+    dependencies:
+      date-fns: 4.1.0
+      react: 19.1.0
+
   react-dom@19.1.0(react@19.1.0):
     dependencies:
       react: 19.1.0
diff --git a/src/app/(home)/logs/glucose/page.tsx b/src/app/(home)/logs/glucose/page.tsx
new file mode 100644
index 0000000..16e9dcb
--- /dev/null
+++ b/src/app/(home)/logs/glucose/page.tsx
@@ -0,0 +1,28 @@
+import GlucoseForm from '~/components/logs/forms/glucose'
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs'
+const GlucoseLogsPage = () => {
+    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>
+    )
+}
+
+export default GlucoseLogsPage
\ No newline at end of file
diff --git a/src/app/api/uploadthing/core.ts b/src/app/api/uploadthing/core.ts
index cb1bded..4f08ac4 100644
--- a/src/app/api/uploadthing/core.ts
+++ b/src/app/api/uploadthing/core.ts
@@ -33,9 +33,6 @@ export const ourFileRouter = {
         })
         .onUploadComplete(async ({ metadata, file }) => {
             // This code RUNS ON YOUR SERVER after upload
-            console.log("Upload complete for userId:", metadata.userId);
-
-            console.log("file url", file.ufsUrl);
 
             // !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback
             return { uploadedBy: metadata.userId };
diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx
index 966794a..e29ee2b 100644
--- a/src/components/app-sidebar.tsx
+++ b/src/components/app-sidebar.tsx
@@ -29,16 +29,16 @@ const data = {
       items: [
         {
           title: "Glucose Logs",
-          url: "/dashboard/logs/glucose",
+          url: "/logs/glucose",
           isActive: true,
         },
         {
           title: "Meal Logs",
-          url: "/dashboard/logs/meal",
+          url: "/logs/meal",
         },
         {
           title: "Medication Logs",
-          url: "/dashboard/logs/medication",
+          url: "/logs/medication",
         },
       ],
     },
diff --git a/src/components/auth/forms/sign-up.tsx b/src/components/auth/forms/sign-up.tsx
index 932dcb5..22c0ed6 100644
--- a/src/components/auth/forms/sign-up.tsx
+++ b/src/components/auth/forms/sign-up.tsx
@@ -48,8 +48,6 @@ const SignUpForm = () => {
         },
     })
 
-    const { data } = useSession()
-
     return (
         <form onSubmit={(e) => {
             e.preventDefault()
diff --git a/src/components/common/user-button.tsx b/src/components/common/user-button.tsx
index 8f6e428..21c66b4 100644
--- a/src/components/common/user-button.tsx
+++ b/src/components/common/user-button.tsx
@@ -1,20 +1,18 @@
 "use client"
 
+import { useQuery } from '@tanstack/react-query'
 import Link from 'next/link'
 import { useRouter } from 'next/navigation'
 import { useMemo } from 'react'
-import type { auth } from '~/auth'
-import { signOut } from '~/lib/auth-client'
+import { getSession, signOut } from '~/lib/auth-client'
 import { cn } from '~/lib/utils'
 import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar'
 import { Button, buttonVariants } from '../ui/button'
-import { useQuery } from '@tanstack/react-query'
-import { getSession } from '~/lib/auth-client'
-
+import { Skeleton } from '../ui/skeleton'
 const UserButton = ({ className, showLogout = false }: { className?: string, showLogout?: boolean }) => {
     const router = useRouter()
 
-    const { data: session } = useQuery({
+    const { data: session, isPending: isFetchSessionPending } = useQuery({
         queryKey: ['session'],
         queryFn: () => {
             return getSession()
@@ -29,13 +27,20 @@ const UserButton = ({ className, showLogout = false }: { className?: string, sho
         return user.name.split(' ').map(name => name[0]).join('')
     }, [user?.name])
 
+    if (isFetchSessionPending) {
+        return <div className='flex gap-4'>
+            <Skeleton className='size-8 rounded-full' />
+            {showLogout && <Skeleton className={cn('h-8', className)} />}
+        </div>
+    }
+
     return (
         <div role="navigation" aria-label="User menu">
             {user?.name ? (
                 <div className='flex gap-4'>
                     <Link href='/settings/profile'>
                         <Avatar aria-label={`Profile picture of ${user.name}`}>
-                            <AvatarImage src={user.image ?? undefined} alt={user.name ?? 'Profile picture'} />
+                            <AvatarImage className='size-10' src={user.image ?? undefined} alt={user.name ?? 'Profile picture'} />
                             <AvatarFallback>
                                 {initials}
                             </AvatarFallback>
diff --git a/src/components/logs/forms/glucose.tsx b/src/components/logs/forms/glucose.tsx
new file mode 100644
index 0000000..585e038
--- /dev/null
+++ b/src/components/logs/forms/glucose.tsx
@@ -0,0 +1,250 @@
+"use client"
+
+import { useForm } from '@tanstack/react-form'
+import dayjs from 'dayjs'
+import { CalendarIcon, Loader2 } from 'lucide-react'
+import { toast } from 'sonner'
+import { Button } from '~/components/ui/button'
+import { Calendar } from '~/components/ui/calendar'
+import { Input } from '~/components/ui/input'
+import { Label } from '~/components/ui/label'
+import { Popover, PopoverContent, PopoverTrigger } from '~/components/ui/popover'
+import {
+    Select,
+    SelectContent,
+    SelectItem,
+    SelectTrigger,
+    SelectValue,
+} from "~/components/ui/select"
+import { Textarea } from '~/components/ui/textarea'
+import { TimePicker } from '~/components/ui/time-picker'
+import { toISOStringWithTimezone } from '~/lib/time-picker-utils'
+import { capitalize, cn } from '~/lib/utils'
+import { glucoseFormSchema } from '~/schemas/logs'
+import { api } from '~/trpc/react'
+
+const GlucoseForm = () => {
+    const { mutate: createGlucoseLog, isPending: isGlucoseLogPending } = api.glucose.create.useMutation({
+        onSuccess: () => {
+            form.reset()
+
+            toast.success("Glucose log created successfully")
+        },
+        onError: (error) => {
+            toast.error(error.message)
+        }
+    })
+
+    const form = useForm({
+        defaultValues: {
+            glucose: 0,
+            type: 'before-meal',
+            date: new Date().toISOString(),
+            units: 'imperial',
+            notes: '',
+        },
+        validators: {
+            onChange: glucoseFormSchema
+        },
+        onSubmit: ({ value }) => {
+            createGlucoseLog({
+                ...value,
+                type: value.type as "before-meal" | "after-meal" | "bedtime" | "fasting" | "random",
+                units: value.units as "imperial" | "metric",
+            })
+        }
+    })
+
+    return (
+        <div className='mt-8'>
+            <form onSubmit={(e) => {
+                e.preventDefault()
+                e.stopPropagation()
+
+                form.handleSubmit()
+            }} className="grid gap-4 mt-3">
+                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+                    <div className="grid gap-2">
+                        <Label htmlFor="glucose">Glucose</Label>
+                        <form.Field
+                            name="glucose"
+                            children={(field) => (
+                                <>
+                                    <Input
+                                        id="glucose"
+                                        type="number"
+                                        placeholder="100"
+                                        value={field.state.value}
+                                        onBlur={field.handleBlur}
+                                        onChange={(e) => field.handleChange(parseInt(e.target.value))}
+                                    />
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="type">Type</Label>
+                        <form.Field
+                            name="type"
+                            children={(field) => (
+                                <>
+                                    <Select
+                                        value={field.state.value}
+                                        defaultValue={field.state.value}
+                                        onValueChange={(e) => field.handleChange(e)}
+                                    >
+                                        <SelectTrigger className='w-full'>
+                                            <SelectValue placeholder="Type" />
+                                        </SelectTrigger>
+                                        <SelectContent>
+                                            {['before-meal', 'after-meal', 'bedtime', 'fasting', 'random'].map((type) => (
+                                                <SelectItem key={type} value={type}>
+                                                    {capitalize(type)}
+                                                </SelectItem>
+                                            ))}
+                                        </SelectContent>
+                                    </Select>
+
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="units">Units</Label>
+                        <form.Field
+                            name="units"
+                            children={(field) => (
+                                <>
+                                    <Select
+                                        value={field.state.value}
+                                        defaultValue={field.state.value}
+                                        onValueChange={(e) => field.handleChange(e)}
+                                    >
+                                        <SelectTrigger className='w-full'>
+                                            <SelectValue placeholder="Units" />
+                                        </SelectTrigger>
+                                        <SelectContent>
+                                            {['imperial', 'metric'].map((type) => (
+                                                <SelectItem key={type} value={type}>
+                                                    {capitalize(type)} - {type === 'imperial' ? 'mg/dL' : 'mmol/L'}
+                                                </SelectItem>
+                                            ))}
+                                        </SelectContent>
+                                    </Select>
+
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="date">Date</Label>
+                        <form.Field
+                            name="date"
+                            children={(field) => {
+                                return (
+                                    <>
+                                        <Popover>
+                                            <PopoverTrigger asChild>
+                                                <Button
+                                                    variant="outline"
+                                                    className={cn(
+                                                        "w-full md:w-[280px] justify-start text-left font-normal",
+                                                        !field.state.value && "text-muted-foreground"
+                                                    )}
+                                                >
+                                                    <CalendarIcon className="mr-2 h-4 w-4" />
+                                                    {field.state.value ? (
+                                                        dayjs(field.state.value).format("DD MMM YYYY HH:mm")
+                                                    ) : (
+                                                        <span>Pick a date</span>
+                                                    )}
+                                                </Button>
+                                            </PopoverTrigger>
+                                            <PopoverContent className="w-auto p-0">
+                                                <Calendar
+                                                    mode="single"
+                                                    selected={field.state.value ? new Date(field.state.value) : undefined}
+                                                    onSelect={(date) => field.handleChange(toISOStringWithTimezone(date ?? new Date()))}
+                                                    initialFocus
+                                                    disabled={(date) => date > new Date()}
+                                                />
+                                                <div className="p-3 border-t border-border">
+                                                    <TimePicker
+                                                        setDate={(date) => field.handleChange(toISOStringWithTimezone(date ?? new Date()))}
+                                                        date={new Date(field.state.value)}
+                                                    />
+                                                </div>
+                                            </PopoverContent>
+                                        </Popover>
+                                        {field.state.meta.errors.map((error, i) => (
+                                            <div key={i} className="text-red-500 text-sm">
+                                                {error?.message}
+                                            </div>
+                                        ))}
+                                    </>
+                                )
+                            }}
+                        />
+                    </div>
+                    <div className="grid gap-2 col-span-1 md:col-span-2 lg:col-span-1">
+                        <Label htmlFor="notes">Notes</Label>
+                        <form.Field
+                            name="notes"
+                            children={(field) => (
+                                <>
+                                    <Textarea
+                                        id="notes"
+                                        placeholder="Notes"
+                                        value={field.state.value}
+                                        onBlur={field.handleBlur}
+                                        onChange={(e) => field.handleChange(e.target.value)}
+                                    />
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                </div>
+                <div>
+                    <form.Subscribe
+                        selector={(state) => [state.canSubmit, state.isSubmitting]}
+                        children={([canSubmit, isSubmitting]) => (
+                            <div className='flex gap-2'>
+                                <Button type='submit' disabled={!canSubmit || isSubmitting || isGlucoseLogPending}>
+                                    {isSubmitting || isGlucoseLogPending ? <Loader2 className='size-4 animate-spin' /> : 'Submit'}
+                                </Button>
+                                <Button type='button' variant={'outline'} onClick={() => {
+                                    form.reset()
+                                }}>
+                                    Reset
+                                </Button>
+                            </div>
+                        )}
+                    />
+                </div>
+            </form>
+        </div>
+    )
+}
+
+export default GlucoseForm
+
diff --git a/src/components/profile/forms/profle-image.tsx b/src/components/profile/forms/profle-image.tsx
index ae9a7cd..4de56f5 100644
--- a/src/components/profile/forms/profle-image.tsx
+++ b/src/components/profile/forms/profle-image.tsx
@@ -56,7 +56,6 @@ const ProfileImage = () => {
             onChange: profileImageSchema
         },
         onSubmit: async ({ value }) => {
-            console.log(value.profileImageUrl)
             await updateUser({
                 image: value.profileImageUrl,
             }, {
diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx
new file mode 100644
index 0000000..204cf8e
--- /dev/null
+++ b/src/components/ui/calendar.tsx
@@ -0,0 +1,75 @@
+"use client"
+
+import * as React from "react"
+import { ChevronLeft, ChevronRight } from "lucide-react"
+import { DayPicker } from "react-day-picker"
+
+import { cn } from "~/lib/utils"
+import { buttonVariants } from "~/components/ui/button"
+
+function Calendar({
+  className,
+  classNames,
+  showOutsideDays = true,
+  ...props
+}: React.ComponentProps<typeof DayPicker>) {
+  return (
+    <DayPicker
+      showOutsideDays={showOutsideDays}
+      className={cn("p-3", className)}
+      classNames={{
+        months: "flex flex-col sm:flex-row gap-2",
+        month: "flex flex-col gap-4",
+        caption: "flex justify-center pt-1 relative items-center w-full",
+        caption_label: "text-sm font-medium",
+        nav: "flex items-center gap-1",
+        nav_button: cn(
+          buttonVariants({ variant: "outline" }),
+          "size-7 bg-transparent p-0 opacity-50 hover:opacity-100"
+        ),
+        nav_button_previous: "absolute left-1",
+        nav_button_next: "absolute right-1",
+        table: "w-full border-collapse space-x-1",
+        head_row: "flex",
+        head_cell:
+          "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
+        row: "flex w-full mt-2",
+        cell: cn(
+          "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
+          props.mode === "range"
+            ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
+            : "[&:has([aria-selected])]:rounded-md"
+        ),
+        day: cn(
+          buttonVariants({ variant: "ghost" }),
+          "size-8 p-0 font-normal aria-selected:opacity-100"
+        ),
+        day_range_start:
+          "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
+        day_range_end:
+          "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
+        day_selected:
+          "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
+        day_today: "bg-accent text-accent-foreground",
+        day_outside:
+          "day-outside text-muted-foreground aria-selected:text-muted-foreground",
+        day_disabled: "text-muted-foreground opacity-50",
+        day_range_middle:
+          "aria-selected:bg-accent aria-selected:text-accent-foreground",
+        day_hidden: "invisible",
+        ...classNames,
+      }}
+      components={{
+        IconLeft: ({ className, ...props }) => (
+          <ChevronLeft className={cn("size-4", className)} {...props} />
+        ),
+        IconRight: ({ className, ...props }) => (
+          <ChevronRight className={cn("size-4", className)} {...props} />
+        ),
+      }}
+      {...props}
+    />
+  )
+}
+
+export { Calendar }
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
new file mode 100644
index 0000000..9be5957
--- /dev/null
+++ b/src/components/ui/popover.tsx
@@ -0,0 +1,48 @@
+"use client"
+
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "~/lib/utils"
+
+function Popover({
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
+  return <PopoverPrimitive.Root data-slot="popover" {...props} />
+}
+
+function PopoverTrigger({
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
+  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
+}
+
+function PopoverContent({
+  className,
+  align = "center",
+  sideOffset = 4,
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
+  return (
+    <PopoverPrimitive.Portal>
+      <PopoverPrimitive.Content
+        data-slot="popover-content"
+        align={align}
+        sideOffset={sideOffset}
+        className={cn(
+          "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
+          className
+        )}
+        {...props}
+      />
+    </PopoverPrimitive.Portal>
+  )
+}
+
+function PopoverAnchor({
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
+  return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
+}
+
+export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
diff --git a/src/components/ui/scroll-area.tsx b/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..424eb4e
--- /dev/null
+++ b/src/components/ui/scroll-area.tsx
@@ -0,0 +1,58 @@
+"use client"
+
+import * as React from "react"
+import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
+
+import { cn } from "~/lib/utils"
+
+function ScrollArea({
+  className,
+  children,
+  ...props
+}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
+  return (
+    <ScrollAreaPrimitive.Root
+      data-slot="scroll-area"
+      className={cn("relative", className)}
+      {...props}
+    >
+      <ScrollAreaPrimitive.Viewport
+        data-slot="scroll-area-viewport"
+        className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
+      >
+        {children}
+      </ScrollAreaPrimitive.Viewport>
+      <ScrollBar />
+      <ScrollAreaPrimitive.Corner />
+    </ScrollAreaPrimitive.Root>
+  )
+}
+
+function ScrollBar({
+  className,
+  orientation = "vertical",
+  ...props
+}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
+  return (
+    <ScrollAreaPrimitive.ScrollAreaScrollbar
+      data-slot="scroll-area-scrollbar"
+      orientation={orientation}
+      className={cn(
+        "flex touch-none p-px transition-colors select-none",
+        orientation === "vertical" &&
+          "h-full w-2.5 border-l border-l-transparent",
+        orientation === "horizontal" &&
+          "h-2.5 flex-col border-t border-t-transparent",
+        className
+      )}
+      {...props}
+    >
+      <ScrollAreaPrimitive.ScrollAreaThumb
+        data-slot="scroll-area-thumb"
+        className="bg-border relative flex-1 rounded-full"
+      />
+    </ScrollAreaPrimitive.ScrollAreaScrollbar>
+  )
+}
+
+export { ScrollArea, ScrollBar }
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx
new file mode 100644
index 0000000..1d0e3ff
--- /dev/null
+++ b/src/components/ui/select.tsx
@@ -0,0 +1,185 @@
+"use client"
+
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
+
+import { cn } from "~/lib/utils"
+
+function Select({
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Root>) {
+  return <SelectPrimitive.Root data-slot="select" {...props} />
+}
+
+function SelectGroup({
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Group>) {
+  return <SelectPrimitive.Group data-slot="select-group" {...props} />
+}
+
+function SelectValue({
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Value>) {
+  return <SelectPrimitive.Value data-slot="select-value" {...props} />
+}
+
+function SelectTrigger({
+  className,
+  size = "default",
+  children,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
+  size?: "sm" | "default"
+}) {
+  return (
+    <SelectPrimitive.Trigger
+      data-slot="select-trigger"
+      data-size={size}
+      className={cn(
+        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+        className
+      )}
+      {...props}
+    >
+      {children}
+      <SelectPrimitive.Icon asChild>
+        <ChevronDownIcon className="size-4 opacity-50" />
+      </SelectPrimitive.Icon>
+    </SelectPrimitive.Trigger>
+  )
+}
+
+function SelectContent({
+  className,
+  children,
+  position = "popper",
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Content>) {
+  return (
+    <SelectPrimitive.Portal>
+      <SelectPrimitive.Content
+        data-slot="select-content"
+        className={cn(
+          "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
+          position === "popper" &&
+            "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
+          className
+        )}
+        position={position}
+        {...props}
+      >
+        <SelectScrollUpButton />
+        <SelectPrimitive.Viewport
+          className={cn(
+            "p-1",
+            position === "popper" &&
+              "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
+          )}
+        >
+          {children}
+        </SelectPrimitive.Viewport>
+        <SelectScrollDownButton />
+      </SelectPrimitive.Content>
+    </SelectPrimitive.Portal>
+  )
+}
+
+function SelectLabel({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Label>) {
+  return (
+    <SelectPrimitive.Label
+      data-slot="select-label"
+      className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
+      {...props}
+    />
+  )
+}
+
+function SelectItem({
+  className,
+  children,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Item>) {
+  return (
+    <SelectPrimitive.Item
+      data-slot="select-item"
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
+        className
+      )}
+      {...props}
+    >
+      <span className="absolute right-2 flex size-3.5 items-center justify-center">
+        <SelectPrimitive.ItemIndicator>
+          <CheckIcon className="size-4" />
+        </SelectPrimitive.ItemIndicator>
+      </span>
+      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
+    </SelectPrimitive.Item>
+  )
+}
+
+function SelectSeparator({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
+  return (
+    <SelectPrimitive.Separator
+      data-slot="select-separator"
+      className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
+      {...props}
+    />
+  )
+}
+
+function SelectScrollUpButton({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
+  return (
+    <SelectPrimitive.ScrollUpButton
+      data-slot="select-scroll-up-button"
+      className={cn(
+        "flex cursor-default items-center justify-center py-1",
+        className
+      )}
+      {...props}
+    >
+      <ChevronUpIcon className="size-4" />
+    </SelectPrimitive.ScrollUpButton>
+  )
+}
+
+function SelectScrollDownButton({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
+  return (
+    <SelectPrimitive.ScrollDownButton
+      data-slot="select-scroll-down-button"
+      className={cn(
+        "flex cursor-default items-center justify-center py-1",
+        className
+      )}
+      {...props}
+    >
+      <ChevronDownIcon className="size-4" />
+    </SelectPrimitive.ScrollDownButton>
+  )
+}
+
+export {
+  Select,
+  SelectContent,
+  SelectGroup,
+  SelectItem,
+  SelectLabel,
+  SelectScrollDownButton,
+  SelectScrollUpButton,
+  SelectSeparator,
+  SelectTrigger,
+  SelectValue,
+}
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..1c7ebff
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "~/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+  return (
+    <textarea
+      data-slot="textarea"
+      className={cn(
+        "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
+        className
+      )}
+      {...props}
+    />
+  )
+}
+
+export { Textarea }
diff --git a/src/components/ui/time-picker-input.tsx b/src/components/ui/time-picker-input.tsx
new file mode 100644
index 0000000..51bdd5e
--- /dev/null
+++ b/src/components/ui/time-picker-input.tsx
@@ -0,0 +1,131 @@
+import { Input } from "~/components/ui/input";
+ 
+import { cn } from "~/lib/utils";
+import React from "react";
+import {
+  type Period,
+  type TimePickerType,
+  getArrowByType,
+  getDateByType,
+  setDateByType,
+} from "~/lib/time-picker-utils";
+ 
+export interface TimePickerInputProps
+  extends React.InputHTMLAttributes<HTMLInputElement> {
+  picker: TimePickerType;
+  date: Date | undefined;
+  setDate: (date: Date | undefined) => void;
+  period?: Period;
+  onRightFocus?: () => void;
+  onLeftFocus?: () => void;
+}
+ 
+const TimePickerInput = React.forwardRef<
+  HTMLInputElement,
+  TimePickerInputProps
+>(
+  (
+    {
+      className,
+      type = "tel",
+      value,
+      id,
+      name,
+      date = new Date(new Date().setHours(0, 0, 0, 0)),
+      setDate,
+      onChange,
+      onKeyDown,
+      picker,
+      period,
+      onLeftFocus,
+      onRightFocus,
+      ...props
+    },
+    ref
+  ) => {
+    const [flag, setFlag] = React.useState<boolean>(false);
+    const [prevIntKey, setPrevIntKey] = React.useState<string>("0");
+ 
+    /**
+     * allow the user to enter the second digit within 2 seconds
+     * otherwise start again with entering first digit
+     */
+    React.useEffect(() => {
+      if (flag) {
+        const timer = setTimeout(() => {
+          setFlag(false);
+        }, 2000);
+ 
+        return () => clearTimeout(timer);
+      }
+    }, [flag]);
+ 
+    const calculatedValue = React.useMemo(() => {
+      return getDateByType(date, picker);
+    }, [date, picker]);
+ 
+    const calculateNewValue = (key: string) => {
+      /*
+       * If picker is '12hours' and the first digit is 0, then the second digit is automatically set to 1.
+       * The second entered digit will break the condition and the value will be set to 10-12.
+       */
+      if (picker === "12hours") {
+        if (flag && calculatedValue.slice(1, 2) === "1" && prevIntKey === "0")
+          return "0" + key;
+      }
+ 
+      return !flag ? "0" + key : calculatedValue.slice(1, 2) + key;
+    };
+ 
+    const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
+      if (e.key === "Tab") return;
+      e.preventDefault();
+      if (e.key === "ArrowRight") onRightFocus?.();
+      if (e.key === "ArrowLeft") onLeftFocus?.();
+      if (["ArrowUp", "ArrowDown"].includes(e.key)) {
+        const step = e.key === "ArrowUp" ? 1 : -1;
+        const newValue = getArrowByType(calculatedValue, step, picker);
+        if (flag) setFlag(false);
+        const tempDate = new Date(date);
+        setDate(setDateByType(tempDate, newValue, picker, period));
+      }
+      if (e.key >= "0" && e.key <= "9") {
+        if (picker === "12hours") setPrevIntKey(e.key);
+ 
+        const newValue = calculateNewValue(e.key);
+        if (flag) onRightFocus?.();
+        setFlag((prev) => !prev);
+        const tempDate = new Date(date);
+        setDate(setDateByType(tempDate, newValue, picker, period));
+      }
+    };
+ 
+    return (
+      <Input
+        ref={ref}
+        id={id || picker}
+        name={name || picker}
+        className={cn(
+          "w-[48px] text-center font-mono text-base tabular-nums caret-transparent focus:bg-accent focus:text-accent-foreground [&::-webkit-inner-spin-button]:appearance-none",
+          className
+        )}
+        value={value || calculatedValue}
+        onChange={(e) => {
+          e.preventDefault();
+          onChange?.(e);
+        }}
+        type={type}
+        inputMode="decimal"
+        onKeyDown={(e) => {
+          onKeyDown?.(e);
+          handleKeyDown(e);
+        }}
+        {...props}
+      />
+    );
+  }
+);
+ 
+TimePickerInput.displayName = "TimePickerInput";
+ 
+export { TimePickerInput };
\ No newline at end of file
diff --git a/src/components/ui/time-picker.tsx b/src/components/ui/time-picker.tsx
new file mode 100644
index 0000000..b1429dc
--- /dev/null
+++ b/src/components/ui/time-picker.tsx
@@ -0,0 +1,48 @@
+"use client";
+ 
+import * as React from "react";
+import { Clock } from "lucide-react";
+import { Label } from "~/components/ui/label";
+import { TimePickerInput } from "~/components/ui/time-picker-input";
+ 
+interface TimePickerProps {
+  date: Date | undefined;
+  setDate: (date: Date | undefined) => void;
+}
+ 
+export function TimePicker({ date, setDate }: TimePickerProps) {
+  const minuteRef = React.useRef<HTMLInputElement>(null);
+  const hourRef = React.useRef<HTMLInputElement>(null);
+ 
+  return (
+    <div className="flex items-end gap-2">
+      <div className="grid gap-1 text-center">
+        <Label htmlFor="hours" className="text-xs">
+          Hours
+        </Label>
+        <TimePickerInput
+          picker="hours"
+          date={date}
+          setDate={setDate}
+          ref={hourRef}
+          onRightFocus={() => minuteRef.current?.focus()}
+        />
+      </div>
+      <div className="grid gap-1 text-center">
+        <Label htmlFor="minutes" className="text-xs">
+          Minutes
+        </Label>
+        <TimePickerInput
+          picker="minutes"
+          date={date}
+          setDate={setDate}
+          ref={minuteRef}
+          onLeftFocus={() => hourRef.current?.focus()}
+        />
+      </div>
+      <div className="flex h-10 items-center">
+        <Clock className="ml-2 h-4 w-4" />
+      </div>
+    </div>
+  );
+}
\ No newline at end of file
diff --git a/src/lib/time-picker-utils.ts b/src/lib/time-picker-utils.ts
new file mode 100644
index 0000000..b6cd567
--- /dev/null
+++ b/src/lib/time-picker-utils.ts
@@ -0,0 +1,222 @@
+/**
+ * regular expression to check for valid hour format (01-23)
+ */
+export function isValidHour(value: string) {
+  return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid 12 hour format (01-12)
+ */
+export function isValid12Hour(value: string) {
+  return /^(0[1-9]|1[0-2])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid minute format (00-59)
+ */
+export function isValidMinuteOrSecond(value: string) {
+  return /^[0-5][0-9]$/.test(value);
+}
+
+type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
+
+export function getValidNumber(
+  value: string,
+  { max, min = 0, loop = false }: GetValidNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+
+  if (!isNaN(numericValue)) {
+    if (!loop) {
+      if (numericValue > max) numericValue = max;
+      if (numericValue < min) numericValue = min;
+    } else {
+      if (numericValue > max) numericValue = min;
+      if (numericValue < min) numericValue = max;
+    }
+    return numericValue.toString().padStart(2, "0");
+  }
+
+  return "00";
+}
+
+export function getValidHour(value: string) {
+  if (isValidHour(value)) return value;
+  return getValidNumber(value, { max: 23 });
+}
+
+export function getValid12Hour(value: string) {
+  if (isValid12Hour(value)) return value;
+  return getValidNumber(value, { min: 1, max: 12 });
+}
+
+export function getValidMinuteOrSecond(value: string) {
+  if (isValidMinuteOrSecond(value)) return value;
+  return getValidNumber(value, { max: 59 });
+}
+
+type GetValidArrowNumberConfig = {
+  min: number;
+  max: number;
+  step: number;
+};
+
+export function getValidArrowNumber(
+  value: string,
+  { min, max, step }: GetValidArrowNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+  if (!isNaN(numericValue)) {
+    numericValue += step;
+    return getValidNumber(String(numericValue), { min, max, loop: true });
+  }
+  return "00";
+}
+
+export function getValidArrowHour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 23, step });
+}
+
+export function getValidArrow12Hour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 1, max: 12, step });
+}
+
+export function getValidArrowMinuteOrSecond(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 59, step });
+}
+
+export function setMinutes(date: Date, value: string) {
+  const minutes = getValidMinuteOrSecond(value);
+  date.setMinutes(parseInt(minutes, 10));
+  return date;
+}
+
+export function setSeconds(date: Date, value: string) {
+  const seconds = getValidMinuteOrSecond(value);
+  date.setSeconds(parseInt(seconds, 10));
+  return date;
+}
+
+export function setHours(date: Date, value: string) {
+  const hours = getValidHour(value);
+  date.setHours(parseInt(hours, 10));
+  return date;
+}
+
+export function set12Hours(date: Date, value: string, period: Period) {
+  const hours = parseInt(getValid12Hour(value), 10);
+  const convertedHours = convert12HourTo24Hour(hours, period);
+  date.setHours(convertedHours);
+  return date;
+}
+
+export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
+export type Period = "AM" | "PM";
+
+export function setDateByType(
+  date: Date,
+  value: string,
+  type: TimePickerType,
+  period?: Period
+) {
+  switch (type) {
+    case "minutes":
+      return setMinutes(date, value);
+    case "seconds":
+      return setSeconds(date, value);
+    case "hours":
+      return setHours(date, value);
+    case "12hours": {
+      if (!period) return date;
+      return set12Hours(date, value, period);
+    }
+    default:
+      return date;
+  }
+}
+
+export function getDateByType(date: Date, type: TimePickerType) {
+  switch (type) {
+    case "minutes":
+      return getValidMinuteOrSecond(String(date.getMinutes()));
+    case "seconds":
+      return getValidMinuteOrSecond(String(date.getSeconds()));
+    case "hours":
+      return getValidHour(String(date.getHours()));
+    case "12hours":
+      const hours = display12HourValue(date.getHours());
+      return getValid12Hour(String(hours));
+    default:
+      return "00";
+  }
+}
+
+export function getArrowByType(
+  value: string,
+  step: number,
+  type: TimePickerType
+) {
+  switch (type) {
+    case "minutes":
+      return getValidArrowMinuteOrSecond(value, step);
+    case "seconds":
+      return getValidArrowMinuteOrSecond(value, step);
+    case "hours":
+      return getValidArrowHour(value, step);
+    case "12hours":
+      return getValidArrow12Hour(value, step);
+    default:
+      return "00";
+  }
+}
+
+/**
+ * handles value change of 12-hour input
+ * 12:00 PM is 12:00
+ * 12:00 AM is 00:00
+ */
+export function convert12HourTo24Hour(hour: number, period: Period) {
+  if (period === "PM") {
+    if (hour <= 11) {
+      return hour + 12;
+    } else {
+      return hour;
+    }
+  } else if (period === "AM") {
+    if (hour === 12) return 0;
+    return hour;
+  }
+  return hour;
+}
+
+/**
+ * time is stored in the 24-hour form,
+ * but needs to be displayed to the user
+ * in its 12-hour representation
+ */
+export function display12HourValue(hours: number) {
+  if (hours === 0 || hours === 12) return "12";
+  if (hours >= 22) return `${hours - 12}`;
+  if (hours % 12 > 9) return `${hours}`;
+  return `0${hours % 12}`;
+}
+
+
+const pad = (n: number) => `${Math.floor(Math.abs(n))}`.padStart(2, '0');
+
+const getTimezoneOffset = (date: Date) => {
+  const tzOffset = -date.getTimezoneOffset();
+  const diff = tzOffset >= 0 ? '+' : '-';
+  return diff + pad(tzOffset / 60) + ':' + pad(tzOffset % 60);
+};
+
+export const toISOStringWithTimezone = (date: Date) => {
+  return date.getFullYear() +
+    '-' + pad(date.getMonth() + 1) +
+    '-' + pad(date.getDate()) +
+    'T' + pad(date.getHours()) +
+    ':' + pad(date.getMinutes()) +
+    ':' + pad(date.getSeconds()) +
+    getTimezoneOffset(date);
+};
\ No newline at end of file
diff --git a/src/schemas/logs.ts b/src/schemas/logs.ts
new file mode 100644
index 0000000..b61264d
--- /dev/null
+++ b/src/schemas/logs.ts
@@ -0,0 +1,11 @@
+import { z } from "zod";
+
+export const glucoseFormSchema = z.object({
+    glucose: z.number({
+        message: "Glucose level must be a number",
+    }).min(0).max(500),
+    date: z.string(),
+    type: z.enum(['before-meal', 'after-meal', 'bedtime', 'fasting', 'random']),
+    units: z.enum(['imperial', 'metric']),
+    notes: z.string().max(500),
+})
\ No newline at end of file
diff --git a/src/server/api/root.ts b/src/server/api/root.ts
index 56e1e0f..3ca9ea6 100644
--- a/src/server/api/root.ts
+++ b/src/server/api/root.ts
@@ -1,13 +1,14 @@
 import { postRouter } from "~/server/api/routers/post";
 import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
-
+import { glucoseRouter } from "~/server/api/routers/glucose";
 /**
  * This is the primary router for your server.
  *
  * All routers added in /api/routers should be manually added here.
  */
 export const appRouter = createTRPCRouter({
-	post: postRouter
+	post: postRouter,
+	glucose: glucoseRouter
 });
 
 // export type definition of API
diff --git a/src/server/api/routers/glucose.ts b/src/server/api/routers/glucose.ts
new file mode 100644
index 0000000..e911735
--- /dev/null
+++ b/src/server/api/routers/glucose.ts
@@ -0,0 +1,35 @@
+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 }) => {
+        try {
+            await db.insert(glucoseLog).values({
+                ...input,
+                date: new Date(input.date),
+                userId: auth.user.id,
+            })
+
+            return {
+                success: true,
+                message: "Glucose log created successfully"
+            }
+        } 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/server/api/trpc.ts b/src/server/api/trpc.ts
index 62f3637..ae97df5 100644
--- a/src/server/api/trpc.ts
+++ b/src/server/api/trpc.ts
@@ -6,9 +6,11 @@
  * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
  * need to use are documented accordingly near the end.
  */
-import { initTRPC } from "@trpc/server";
+import { initTRPC, TRPCError } from "@trpc/server";
+import { headers } from "next/headers";
 import superjson from "superjson";
 import { ZodError } from "zod";
+import { auth } from "~/auth";
 
 import { db } from "~/server/db";
 
@@ -27,6 +29,9 @@ import { db } from "~/server/db";
 export const createTRPCContext = async (opts: { headers: Headers }) => {
 	return {
 		db,
+		auth: await auth.api.getSession({
+			headers: await headers()
+		}),
 		...opts,
 	};
 };
@@ -104,3 +109,17 @@ const timingMiddleware = t.middleware(async ({ next, path }) => {
  * are logged in.
  */
 export const publicProcedure = t.procedure.use(timingMiddleware);
+
+export const isAuthed = t.middleware(async ({ next, ctx }) => {
+	if (!ctx.auth?.user.id) {
+		throw new TRPCError({ code: "UNAUTHORIZED", message: "Unauthorized" });
+	}
+	
+	return next({
+		ctx: {
+			auth: ctx.auth
+		}
+	});
+});
+
+export const protectedProcedure = t.procedure.use(isAuthed);
diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts
index b4043aa..d7698c9 100644
--- a/src/server/db/schema.ts
+++ b/src/server/db/schema.ts
@@ -78,3 +78,18 @@ export const verification = createTable("verification", (d) => ({
 	updatedAt: d.timestamp('updated_at')
 }))
 
+export const glucoseLog = createTable("glucose_log", (d) => ({
+	id: d.integer('id').primaryKey().generatedByDefaultAsIdentity(),
+	userId: d.text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
+	glucose: d.integer('glucose').notNull(),
+	date: d.timestamp('date').notNull(),
+	type: d.text('type').notNull(),
+	units: d.text('units').notNull(),
+	notes: d.text('notes'),
+	createdAt: d
+		.timestamp({ withTimezone: true })
+		.default(sql`CURRENT_TIMESTAMP`)
+		.notNull(),
+	updatedAt: d.timestamp({ withTimezone: true }).$onUpdate(() => new Date()),
+}))
+


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

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

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

`9-9`: **Consider default tab selection**

The tabs default to the 'upload' tab. Consider if showing the 'history' tab first might provide better user experience, as users may want to view their existing logs before adding new ones.

```diff
-<Tabs defaultValue='upload'>
+<Tabs defaultValue='history'>
```

---

`22-23`: **Remove unnecessary whitespace**

There's extra whitespace on line 22 that should be removed for consistency.

```diff
-                
```

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

`25-27`: **Consider standardizing date utility libraries**

The PR adds both `date-fns` and `dayjs`, which serve similar purposes for date manipulation. Using multiple date libraries could lead to inconsistent date handling patterns and increase bundle size.

Consider standardizing on one date utility library (either date-fns or dayjs) to maintain consistency throughout the codebase and optimize bundle size.


Also applies to: 42-43, 50-50

</blockquote></details>
<details>
<summary>src/components/common/user-button.tsx (2)</summary><blockquote>

`30-35`: **Ensure consistent sizing between skeleton and actual avatar.**

The skeleton for the avatar uses `size-8` class, but the actual avatar uses `size-10`. These should be consistent:

```diff
-            <Skeleton className='size-8 rounded-full' />
+            <Skeleton className='size-10 rounded-full' />
```

---

`38-38`: **Replace div with semantic nav element.**

For better accessibility, replace the div with a semantic `<nav>` element as suggested by static analysis:

```diff
-        <div role="navigation" aria-label="User menu">
+        <nav aria-label="User menu">
```

And remember to update the closing tag as well.

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

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

[error] 38-38: The elements with the following roles can be changed to the following elements:
<nav>


For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)

</details>

</details>

</blockquote></details>
<details>
<summary>src/components/ui/time-picker.tsx (1)</summary><blockquote>

`17-46`: **Consider enhancing accessibility attributes.**

The component has good basic structure, but could benefit from additional accessibility enhancements:

```diff
-  <div className="flex items-end gap-2">
+  <div 
+    className="flex items-end gap-2"
+    role="group"
+    aria-label="Time picker"
+  >
```

Also consider adding aria-valuemin, aria-valuemax, and aria-valuenow to indicate the numeric input ranges for screen readers.

</blockquote></details>
<details>
<summary>src/server/db/schema.ts (1)</summary><blockquote>

`81-94`: **Consider adding constraints for the glucose value.**

The table structure for glucose logs is well-defined with appropriate relationships and column types. However, consider adding min/max value constraints for the glucose column, as blood glucose levels typically fall within specific medical ranges.


```diff
-	glucose: d.integer('glucose').notNull(),
+	glucose: d.integer('glucose').notNull().check(sql`glucose >= 0 AND glucose <= 1000`),
```

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

`37-42`: **Consider extracting complex class logic to improve readability.**

The cell classNames contain complex conditional logic that might be more maintainable if extracted to a separate function or variable.


```diff
+ const getCellClassName = (mode?: string) => cn(
+   "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
+   mode === "range"
+     ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
+     : "[&:has([aria-selected])]:rounded-md"
+ );

  cell: cn(
-   "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
-   props.mode === "range"
-     ? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
-     : "[&:has([aria-selected])]:rounded-md"
+   getCellClassName(props.mode)
  ),
```

</blockquote></details>
<details>
<summary>src/components/ui/scroll-area.tsx (2)</summary><blockquote>

`3-4`: **Consider using `import type` for type-only imports.**

The React import is currently only used for type references. Using `import type` ensures the imports are removed during compilation and improves bundle size.

```diff
-import * as React from "react"
+import type * as React from "react"
 import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
```

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

---

`21-21`: **Use descriptive class name for improved readability.**

The class name `size-full` is a shorthand for width and height. For better readability, consider using the more explicit `w-full h-full` instead.

```diff
-        className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
+        className="focus-visible:ring-ring/50 w-full h-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
```

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

`38-56`: **Initialize glucose with reasonable default value.**

The current default value for glucose is 0, which might not be a valid or common glucose reading. Consider initializing with a more typical default value or with an empty string/null to require user input.

```diff
 const form = useForm({
     defaultValues: {
-        glucose: 0,
+        glucose: '', // Empty to require user input or set a typical value like 100
         type: 'before-meal',
         date: new Date().toISOString(),
         units: 'imperial',
         notes: '',
     },
```

---

`80-84`: **Use error message ID for error message keys instead of array index.**

Using array indices as React keys can lead to unexpected behavior when the array changes. Consider generating a unique key based on the error message content.

```diff
-{field.state.meta.errors.map((error, i) => (
-    <div key={i} className="text-red-500 text-sm">
+{field.state.meta.errors.map((error) => (
+    <div key={error?.message || Math.random()} className="text-red-500 text-sm">
         {error?.message}
     </div>
 ))}
```


Also applies to: 112-116, 144-148, 193-197, 216-220

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

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

[error] 81-81: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

</details>

</details>

---

`226-242`: **Use form state for disabling Submit button rather than checking isGlucoseLogPending twice.**

The Submit button's disabled state includes a redundant check of `isGlucoseLogPending` which is already part of the check for `isSubmitting`.

```diff
 <Button type='submit' disabled={!canSubmit || isSubmitting || isGlucoseLogPending}>
-    {isSubmitting || isGlucoseLogPending ? <Loader2 className='size-4 animate-spin' /> : 'Submit'}
+    {isSubmitting || isGlucoseLogPending ? <Loader2 className='w-4 h-4 animate-spin' /> : 'Submit'}
 </Button>
```

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

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

[error] 229-229: 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>
<details>
<summary>src/components/ui/time-picker-input.tsx (2)</summary><blockquote>

`72-78`: **Use template literals instead of string concatenation.**

Replace string concatenation with template literals for better readability and consistency.

```diff
 if (picker === "12hours") {
   if (flag && calculatedValue.slice(1, 2) === "1" && prevIntKey === "0")
-     return "0" + key;
+     return `0${key}`;
 }

-return !flag ? "0" + key : calculatedValue.slice(1, 2) + key;
+return !flag ? `0${key}` : `${calculatedValue.slice(1, 2)}${key}`;
```

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

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

[error] 74-74: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

---

[error] 77-77: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>

</details>

---

`103-126`: **Consider adding ARIA attributes for better accessibility.**

The time picker input would benefit from additional ARIA attributes to improve accessibility for screen readers.

```diff
 <Input
   ref={ref}
   id={id || picker}
   name={name || picker}
+  aria-label={`${picker} input`}
+  role="spinbutton"
+  aria-valuemin={picker === "hours" ? (period === "AM" ? 1 : 0) : 0}
+  aria-valuemax={picker === "hours" ? (period === "AM" ? 12 : 23) : 59}
+  aria-valuenow={parseInt(calculatedValue, 10)}
   className={cn(
     "w-[48px] text-center font-mono text-base tabular-nums caret-transparent focus:bg-accent focus:text-accent-foreground [&::-webkit-inner-spin-button]:appearance-none",
     className
   )}
```

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

`3-4`: **Consider using `import type` for type-only imports.**

The React import is only used for type references. Using `import type` ensures the imports are removed during compilation and improves bundle size.

```diff
-import * as React from "react"
+import type * as React from "react"
 import * as PopoverPrimitive from "@radix-ui/react-popover"
```

<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/ui/select.tsx (1)</summary><blockquote>

`1-8`: **Consider updating imports to use `import type` for type-only imports.**

The static analysis tools have detected that some imports are only used as types. Using `import type` ensures they are removed by the compiler, optimizing bundle size.

```diff
"use client"

-import * as React from "react"
+import { type ComponentProps } from "react"
+import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"

import { cn } from "~/lib/utils"
```

<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/lib/time-picker-utils.ts (2)</summary><blockquote>

`179-191`: **Simplify conditional logic in convert12HourTo24Hour function.**

Remove unnecessary else clauses as they're redundant when previous branches have return statements.

```diff
export function convert12HourTo24Hour(hour: number, period: Period) {
  if (period === "PM") {
    if (hour <= 11) {
      return hour + 12;
-    } else {
-      return hour;
    }
+    return hour;
-  } else if (period === "AM") {
+  }
+  if (period === "AM") {
    if (hour === 12) return 0;
    return hour;
  }
  return hour;
}
```

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

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

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

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

---

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

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

---

`205-222`: **Use template literals for string formatting.**

Replace string concatenation with template literals for better readability and maintainability.

```diff
const pad = (n: number) => `${Math.floor(Math.abs(n))}`.padStart(2, '0');

const getTimezoneOffset = (date: Date) => {
  const tzOffset = -date.getTimezoneOffset();
  const diff = tzOffset >= 0 ? '+' : '-';
-  return diff + pad(tzOffset / 60) + ':' + pad(tzOffset % 60);
+  return `${diff}${pad(tzOffset / 60)}:${pad(tzOffset % 60)}`;
};

export const toISOStringWithTimezone = (date: Date) => {
-  return date.getFullYear() +
-    '-' + pad(date.getMonth() + 1) +
-    '-' + pad(date.getDate()) +
-    'T' + pad(date.getHours()) +
-    ':' + pad(date.getMinutes()) +
-    ':' + pad(date.getSeconds()) +
-    getTimezoneOffset(date);
+  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}${getTimezoneOffset(date)}`;
};
```

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

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

[error] 211-211: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

---

[error] 215-221: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>

</details>

</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 8d581e57166566c596553a7fe5680a23c462e340 and 3abea6a62498e2900c164c486b284c5feb260965.

</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 (21)</summary>

* `package.json` (2 hunks)
* `src/app/(home)/logs/glucose/page.tsx` (1 hunks)
* `src/app/api/uploadthing/core.ts` (0 hunks)
* `src/components/app-sidebar.tsx` (1 hunks)
* `src/components/auth/forms/sign-up.tsx` (0 hunks)
* `src/components/common/user-button.tsx` (2 hunks)
* `src/components/logs/forms/glucose.tsx` (1 hunks)
* `src/components/profile/forms/profle-image.tsx` (0 hunks)
* `src/components/ui/calendar.tsx` (1 hunks)
* `src/components/ui/popover.tsx` (1 hunks)
* `src/components/ui/scroll-area.tsx` (1 hunks)
* `src/components/ui/select.tsx` (1 hunks)
* `src/components/ui/textarea.tsx` (1 hunks)
* `src/components/ui/time-picker-input.tsx` (1 hunks)
* `src/components/ui/time-picker.tsx` (1 hunks)
* `src/lib/time-picker-utils.ts` (1 hunks)
* `src/schemas/logs.ts` (1 hunks)
* `src/server/api/root.ts` (1 hunks)
* `src/server/api/routers/glucose.ts` (1 hunks)
* `src/server/api/trpc.ts` (3 hunks)
* `src/server/db/schema.ts` (1 hunks)

</details>

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

* src/app/api/uploadthing/core.ts
* src/components/profile/forms/profle-image.tsx
* src/components/auth/forms/sign-up.tsx

</details>

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

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

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

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

* `createTRPCRouter` (79-79)

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

* `postRouter` (6-30)

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

* `glucoseRouter` (6-30)

</details>

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

<details>
<summary>src/server/api/trpc.ts (2)</summary>

* `createTRPCRouter` (79-79)
* `protectedProcedure` (125-125)

</details>
<details>
<summary>src/schemas/logs.ts (1)</summary>

* `glucoseFormSchema` (3-9)

</details>
<details>
<summary>src/server/db/index.ts (1)</summary>

* `db` (18-18)

</details>
<details>
<summary>src/server/db/schema.ts (1)</summary>

* `glucoseLog` (81-94)

</details>

</blockquote></details>
<details>
<summary>src/components/ui/time-picker.tsx (2)</summary><blockquote>

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

* `Label` (24-24)

</details>
<details>
<summary>src/components/ui/time-picker-input.tsx (1)</summary>

* `TimePickerInput` (131-131)

</details>

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

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

* `cn` (4-6)

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

* `buttonVariants` (59-59)

</details>

</blockquote></details>
<details>
<summary>src/components/ui/scroll-area.tsx (1)</summary><blockquote>

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

* `cn` (4-6)

</details>

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

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

* `cn` (4-6)

</details>

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

<details>
<summary>src/auth.ts (1)</summary>

* `auth` (7-35)

</details>

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

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

* `cn` (4-6)

</details>

</blockquote></details>
<details>
<summary>src/components/ui/select.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/components/common/user-button.tsx</summary>

[error] 38-38: The elements with the following roles can be changed to the following elements:
<nav>


For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)

</details>
<details>
<summary>src/components/ui/calendar.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/ui/scroll-area.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/ui/textarea.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/logs/forms/glucose.tsx</summary>

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

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

(lint/correctness/noChildrenProp)

---

[error] 78-78: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 81-81: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 113-113: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 145-145: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 194-194: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 217-217: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

[error] 229-229: 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/ui/time-picker-input.tsx</summary>

[error] 74-74: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

---

[error] 77-77: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>
<details>
<summary>src/components/ui/popover.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/lib/time-picker-utils.ts</summary>

[error] 148-148: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.


(lint/correctness/noSwitchDeclarations)

---

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

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

---

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

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

---

[error] 211-211: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

---

[error] 215-221: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

---

[error] 28-28: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 30-30: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.


(lint/suspicious/noGlobalIsNan)

---

[error] 69-69: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 70-70: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.


(lint/suspicious/noGlobalIsNan)

---

[error] 91-91: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 97-97: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 103-103: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 108-108: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

</details>
<details>
<summary>src/components/ui/select.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>

</details>

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

<details>
<summary>src/components/app-sidebar.tsx (1)</summary>

`32-32`: **Navigation URLs have been updated appropriately**

The removal of the "/dashboard" prefix from all log-related URLs creates a cleaner URL structure and aligns with the new route organization. This change is consistent across all log types (glucose, meal, and medication).



Also applies to: 37-37, 41-41

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

`3-3`: **Good integration of the glucose router**

The glucose router has been properly imported and added to the app router following the existing pattern. This enables the API to handle glucose log-related operations.



Also applies to: 10-11

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

`1-28`: **Good implementation of the glucose logs page with tabbed interface**

The page structure is clean and follows good React patterns with a tabbed interface that separates history and upload functionality.

---

`14-18`: **History tab contains placeholder content**

The History tab currently only displays a heading without actual log data. This appears to be intended for future implementation.

Is the implementation of the history tab functionality planned for a future PR? If it's meant to be completed in this PR, consider implementing the glucose log history display.

</details>
<details>
<summary>src/components/common/user-button.tsx (3)</summary>

`3-11`: **LGTM! Clean import organization and Skeleton component addition.**

The imports are well organized, and the addition of the Skeleton component enables proper loading state handling.

---

`15-35`: **LGTM! Clear loading state implementation.**

Renaming `isPending` to `isFetchSessionPending` greatly improves clarity. The loading skeleton provides a smooth user experience while waiting for session data.

---

`43-43`: **LGTM! Explicit sizing for avatar image.**

Adding the `size-10` class ensures consistent sizing for the avatar.

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

`6-18`: **LGTM! Well-structured TRPC mutation with proper validation.**

The mutation is well-structured, using the schema for validation, properly converting the date string to a Date object, and associating the log with the authenticated user. The success response includes helpful information.

</details>
<details>
<summary>src/components/ui/time-picker.tsx (2)</summary>

`17-46`: **LGTM! Well-structured and keyboard-navigable time picker.**

The component has a clean design with good keyboard navigation between hours and minutes inputs using the focus management. The layout is responsive and includes a visual clock indicator.

---

`20-22`: <details>
<summary>✅ Verification successful</summary>

**Verify htmlFor attribute matches input id.**

Ensure the htmlFor="hours" attribute matches the actual id of the hours input from the TimePickerInput component. Same for the minutes input.

---

🏁 Script executed:

```shell
#!/bin/bash
# Find the TimePickerInput component to check id implementations
grep -n "id=" src/components/ui/time-picker-input.tsx
```

Length of output: 83

---

🏁 Script executed:

```shell
#!/bin/bash
# Show lines around Label htmlFor in time-picker.tsx
echo "=== Label htmlFor Context ==="
rg -n 'Label htmlFor' -C3 src/components/ui/time-picker.tsx

# Show usage of TimePickerInput with picker/id props
echo "=== TimePickerInput Usage ==="
rg -n 'TimePickerInput' -C3 src/components/ui/time-picker.tsx
```

Length of output: 1386

---

🏁 Script executed:

```shell
#!/bin/bash
# Display the TimePickerInput component for context
rg -n 'export function TimePickerInput' -A50 -B5 src/components/ui/time-picker-input.tsx
```

Length of output: 90

---



</details>

**No action needed—htmlFor attributes are correct**

The `TimePickerInput` component sets its `id` to `id || picker`. In your usage you pass `picker="hours"` and `picker="minutes"`, so the inputs receive IDs “hours” and “minutes” respectively, matching the `<Label htmlFor="…">` values.

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

`1-18`: **Looks good - clean and accessible textarea component.**

The Textarea implementation follows React best practices with proper prop forwarding and accessibility attributes. The component is well-structured, using the `cn` utility to merge custom classNames with base styles.

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

</details>
<details>
<summary>src/server/api/trpc.ts (3)</summary>

`32-34`: **Good implementation of auth session in the tRPC context.**

The implementation properly fetches the user session using auth.api.getSession and adds it to the context.

---

`113-123`: **Authentication middleware looks robust.**

The isAuthed middleware correctly checks for user authentication and throws an appropriate UNAUTHORIZED error when the user is not authenticated.

---

`125-125`: **Well-defined protected procedure.**

The protectedProcedure effectively combines the base procedure with the authentication middleware, following the tRPC best practices for securing endpoints.

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

`1-75`: **Well-implemented calendar component with thorough styling.**

The Calendar component is a well-crafted wrapper around DayPicker with extensive customization options. The styling is comprehensive and covers all interactive states including selection, range, disabled days, and hover effects.

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

</details>
<details>
<summary>src/components/ui/scroll-area.tsx (2)</summary>

`8-29`: **LGTM! Well-structured ScrollArea component.**

The component properly wraps Radix UI's ScrollArea primitives with appropriate data attributes and styling. The use of `cn` for class name composition and proper prop forwarding is excellent.

---

`31-56`: **LGTM! ScrollBar implementation with flexible orientation support.**

The ScrollBar component elegantly handles both vertical and horizontal orientations with conditional class application. The component is well-structured with appropriate data attributes and styling.

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

`95-110`: **LGTM! Well-structured Select component for glucose types.**

The type selection is well-implemented with proper value handling and capitalization of display values.

---

`177-191`: **LGTM! Well-implemented date and time picker with future date restriction.**

The date picker implementation with calendar and time picker is well done. The disabled property correctly prevents selecting future dates.

</details>
<details>
<summary>src/components/ui/time-picker-input.tsx (1)</summary>

`80-101`: **LGTM! Comprehensive keyboard handling for time input.**

The keyboard event handling logic is well-implemented with proper handling of arrow keys for navigation and value adjustment, digit keys for input, and focus management.

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

`8-18`: **LGTM! Clean implementation of Popover and PopoverTrigger components.**

The Popover and PopoverTrigger components are well-implemented with proper data attributes and prop forwarding.

---

`42-46`: **LGTM! Clean implementation of PopoverAnchor component.**

The PopoverAnchor component is well-implemented with proper data attributes and prop forwarding.

</details>
<details>
<summary>src/components/ui/select.tsx (8)</summary>

`9-13`: **LGTM! The base Select component is well implemented.**

This component correctly wraps the Radix UI Select primitive and adds a data-slot attribute for styling purposes while forwarding all props.

---

`15-25`: **LGTM! SelectGroup and SelectValue components look good.**

Both components correctly wrap their respective Radix UI primitives with data-slot attributes and properly forward props.

---

`27-51`: **Well-designed SelectTrigger with flexible sizing options.**

The component is well-structured with a size prop that supports "sm" and "default" options. The styling is comprehensive and includes proper focus states, disabled states, and other interactive elements.

---

`53-86`: **LGTM! SelectContent handles positioning and animations well.**

The SelectContent component properly uses portal rendering for better stacking context management, includes animation states, and handles positioning with a "popper" default. The inclusion of scroll buttons for overflow handling is a nice touch.

---

`88-99`: **LGTM! SelectLabel is properly implemented.**

The component wraps the Radix UI primitive with appropriate styling and data attributes.

---

`101-123`: **LGTM! SelectItem provides good visual feedback.**

The SelectItem component includes proper styling for focus states, disabled states, and visual indicators for selection. The CheckIcon is appropriately positioned and sized.

---

`125-136`: **LGTM! SelectSeparator is correctly implemented.**

The component provides visual separation with appropriate styling.

---

`138-172`: **LGTM! Scroll button components are well implemented.**

Both SelectScrollUpButton and SelectScrollDownButton are correctly structured with appropriate styling and icons, providing good visual cues for scrolling actions.

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

`1-20`: **LGTM! Time validation functions are well implemented.**

The regex patterns for validating hour, 12-hour, and minute/second formats are correct and well-documented with JSDoc comments.

</details>

</blockquote></details>

</details>

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

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

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

`73-79`: _⚠️ Potential issue_

**Fix input parsing to handle empty input and prevent NaN values.**

The current implementation will parse an empty input to `NaN` when using `parseInt`. This could lead to validation errors or unexpected behavior.


```diff
<Input
    id="glucose"
    type="number"
    placeholder="100"
    value={field.state.value}
    onBlur={field.handleBlur}
-    onChange={(e) => field.handleChange(parseInt(e.target.value))}
+    onChange={(e) => {
+        const value = e.target.value.trim();
+        field.handleChange(value === '' ? '' : Number.parseInt(value, 10));
+    }}
/>
```

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

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

[error] 79-79: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

</details>

</details>

---

`128-143`: _🛠️ Refactor suggestion_

**Add unit conversion support for glucose readings.**

The form allows switching between imperial (mg/dL) and metric (mmol/L) units, but doesn't automatically convert the glucose value.


```diff
<Select
    value={field.state.value}
    defaultValue={field.state.value}
-    onValueChange={(e) => field.handleChange(e)}
+    onValueChange={(e) => {
+        field.handleChange(e);
+        // Convert glucose value if units changed
+        const glucoseField = form.getFieldValue('glucose');
+        const glucoseValue = glucoseField.state.value;
+        if (typeof glucoseValue === 'number' && !isNaN(glucoseValue)) {
+            if (e === 'metric' && field.state.value === 'imperial') {
+                // Convert from mg/dL to mmol/L: divide by 18
+                form.setFieldValue('glucose', Math.round((glucoseValue / 18) * 10) / 10);
+            } else if (e === 'imperial' && field.state.value === 'metric') {
+                // Convert from mmol/L to mg/dL: multiply by 18
+                form.setFieldValue('glucose', Math.round(glucoseValue * 18));
+            }
+        }
+    }}
>
```

</blockquote></details>

</blockquote></details>

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

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

`27-32`: **Simplify the error handling code.**

The `else` clause is unnecessary since the previous branch already throws an error, which will exit the function.

```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"
-                })
+            }
+            // This will only execute if error is not an Error object
+            throw new TRPCError({
+                code: "INTERNAL_SERVER_ERROR",
+                message: "An unknown error occurred"
+            })
            }
```

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

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

[error] 27-32: 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/forms/glucose.tsx (1)</summary><blockquote>

`81-85`: **Avoid using array index as key for error messages.**

Using an array index as a key can cause issues with React's reconciliation when items are added or removed.

```diff
-{field.state.meta.errors.map((error, i) => (
-    <div key={i} className="text-red-500 text-sm">
+{field.state.meta.errors.map((error, i) => (
+    <div key={`glucose-error-${i}`} className="text-red-500 text-sm">
        {error?.message}
    </div>
))}
```

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

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

[error] 82-82: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

</details>

</details>

</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 3abea6a62498e2900c164c486b284c5feb260965 and 7322365309fe5cfad9cd6b6a4cf49362c1719c63.

</details>

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

* `src/components/logs/forms/glucose.tsx` (1 hunks)
* `src/components/ui/time-picker.tsx` (1 hunks)
* `src/schemas/logs.ts` (1 hunks)
* `src/server/api/routers/glucose.ts` (1 hunks)

</details>

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

* src/schemas/logs.ts

</details>

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

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

<details>
<summary>src/components/ui/time-picker.tsx (2)</summary><blockquote>

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

* `Label` (24-24)

</details>
<details>
<summary>src/components/ui/time-picker-input.tsx (1)</summary>

* `TimePickerInput` (131-131)

</details>

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

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

* `api` (25-25)

</details>
<details>
<summary>src/schemas/logs.ts (1)</summary>

* `glucoseFormSchema` (3-11)

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

* `Label` (24-24)

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

* `Input` (21-21)

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

* `Select` (175-175)
* `SelectTrigger` (183-183)
* `SelectValue` (184-184)
* `SelectContent` (176-176)
* `SelectItem` (178-178)

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

* `capitalize` (17-19)
* `cn` (4-6)

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

* `Popover` (48-48)
* `PopoverTrigger` (48-48)
* `PopoverContent` (48-48)

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

* `Button` (59-59)

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

* `Calendar` (75-75)

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

* `toISOStringWithTimezone` (214-222)

</details>
<details>
<summary>src/components/ui/time-picker.tsx (1)</summary>

* `TimePicker` (13-48)

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

* `Textarea` (18-18)

</details>

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

<details>
<summary>src/server/api/trpc.ts (2)</summary>

* `createTRPCRouter` (79-79)
* `protectedProcedure` (125-125)

</details>
<details>
<summary>src/schemas/logs.ts (1)</summary>

* `glucoseFormSchema` (3-11)

</details>
<details>
<summary>src/server/db/schema.ts (1)</summary>

* `glucoseLog` (81-94)

</details>

</blockquote></details>

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

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

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

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

(lint/correctness/noChildrenProp)

---

[error] 79-79: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 82-82: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 114-114: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 146-146: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 195-195: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

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

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

(lint/correctness/noChildrenProp)

---

[error] 218-218: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

---

[error] 230-230: 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/server/api/routers/glucose.ts</summary>

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

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

</details>

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

<details>
<summary>src/components/ui/time-picker.tsx (1)</summary>

`1-48`: **Well-designed time picker component with good accessibility.**

The component is well-structured with proper keyboard navigation between hours and minutes inputs. The use of refs for focus management and htmlFor attributes on labels enhances accessibility. The component follows a clean layout pattern with appropriate labels and icons.

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

`1-36`: **Well-structured router with proper error handling.**

The glucose router is correctly implemented with input validation, authentication via `protectedProcedure`, proper database interactions, and comprehensive error handling.

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

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

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

Unsafe fix: Omit the else clause.


(lint/style/noUselessElse)

</details>

</details>

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

`26-56`: **Good form structure with proper state management and validation.**

The form is well-structured using TanStack Form with proper validation via Zod schema. The mutation handling with success and error callbacks is implemented correctly. The default values are set appropriately.

---

`227-243`: **Well-implemented submit and reset functionality.**

The form's submit button is properly disabled based on form state and API request status. The loading indicator and reset button are well-implemented. Good use of the form subscription API to react to form state changes.

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

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

[error] 230-230: 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>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-11T21:09:07Z

Review comments:
diff_hunk: @@ -0,0 +1,9 @@
+import { z } from "zod";
+
+export const glucoseFormSchema = z.object({
+    glucose: z.number().min(0).max(500),
+    date: z.string(),
+    type: z.enum(['before-meal', 'after-meal', 'bedtime', 'fasting', 'random']),
+    units: z.enum(['imperial', 'metric']),
+    notes: z.string(),
+})
path: src/schemas/logs.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider improving schema validation for better type safety and user experience.**

The schema has a good foundation, but a few improvements would make it more robust:

1. The `date` field is currently just validated as a string. Consider parsing it to a Date object or validating the date format:
   ```diff
   -date: z.string(),
   +date: z.string().refine(str => !isNaN(Date.parse(str)), {
   +  message: "Invalid date format"
   +}),
   ```

2. The `notes` field is likely optional in real usage. Consider making it optional with a max length:
   ```diff
   -notes: z.string(),
   +notes: z.string().max(500).optional().default(''),
   ```




<!-- 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
export const glucoseFormSchema = z.object({
    glucose: z.number().min(0).max(500),
    date: z.string().refine(str => !isNaN(Date.parse(str)), {
      message: "Invalid date format"
    }),
    type: z.enum(['before-meal', 'after-meal', 'bedtime', 'fasting', 'random']),
    units: z.enum(['imperial', 'metric']),
    notes: z.string().max(500).optional().default(''),
})
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:57Z
author_association: NONE
diff_hunk: @@ -0,0 +1,30 @@
+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 }) => {
+        try {
+            await db.insert(glucoseLog).values({
+                ...input,
+                date: new Date(input.date),
+                userId: auth.user.id,
+            })
+
+            return {
+                success: true,
+                message: "Glucose log created successfully"
+            }
+        } catch (error) {
+            if (error instanceof TRPCError) throw error;
+
+            if (error instanceof Error) {
+                throw new TRPCError({
+                    code: "INTERNAL_SERVER_ERROR",
+                    message: error.message
+                })
+            }
+        }
path: src/server/api/routers/glucose.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Improve error handling completeness.**

The error handling looks good but is missing a default case for non-Error objects and doesn't guarantee a return value in all code paths. Consider:

```diff
        } 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"
+               })
            }
        }
```




<!-- 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
        } 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"
                })
            }
        }
`````

</details>

<!-- suggestion_end -->

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

✅ Addressed in commit 7322365
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,48 @@
+"use client";
+ 
+import * as React from "react";
+import { Clock } from "lucide-react";
+import { Label } from "~/components/ui/label";
+import { TimePickerInput } from "~/components/ui/time-picker-input";
+ 
+interface TimePickerDemoProps {
+  date: Date | undefined;
+  setDate: (date: Date | undefined) => void;
+}
+ 
+export function TimePickerDemo({ date, setDate }: TimePickerDemoProps) {
path: src/components/ui/time-picker.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Rename component and interface for production use.**

The component name `TimePickerDemo` and interface `TimePickerDemoProps` suggest this is a demo/example component, but it appears to be used in production code. Consider renaming:

```diff
-interface TimePickerDemoProps {
+interface TimePickerProps {
   date: Date | undefined;
   setDate: (date: Date | undefined) => void;
 }

-export function TimePickerDemo({ date, setDate }: TimePickerDemoProps) {
+export function TimePicker({ date, setDate }: TimePickerProps) {
```




<!-- 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
interface TimePickerProps {
  date: Date | undefined;
  setDate: (date: Date | undefined) => void;
}

export function TimePicker({ date, setDate }: TimePickerProps) {
`````

</details>

<!-- suggestion_end -->

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

✅ Addressed in commit 7322365
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,249 @@
+"use client"
+
+import { useForm } from '@tanstack/react-form'
+import dayjs from 'dayjs'
+import { CalendarIcon, Loader2 } from 'lucide-react'
+import { toast } from 'sonner'
+import { Button } from '~/components/ui/button'
+import { Calendar } from '~/components/ui/calendar'
+import { Input } from '~/components/ui/input'
+import { Label } from '~/components/ui/label'
+import { Popover, PopoverContent, PopoverTrigger } from '~/components/ui/popover'
+import {
+    Select,
+    SelectContent,
+    SelectItem,
+    SelectTrigger,
+    SelectValue,
+} from "~/components/ui/select"
+import { Textarea } from '~/components/ui/textarea'
+import { TimePickerDemo } from '~/components/ui/time-picker'
+import { toISOStringWithTimezone } from '~/lib/time-picker-utils'
+import { capitalize, cn } from '~/lib/utils'
+import { glucoseFormSchema } from '~/schemas/logs'
+import { api } from '~/trpc/react'
+
+const GlucoseForm = () => {
+    const { mutate: createGlucoseLog, isPending: isGlucoseLogPending } = api.glucose.create.useMutation({
+        onSuccess: () => {
+            form.reset()
+
+            toast.success("Glucose log created successfully")
+        },
+        onError: (error) => {
+            toast.error(error.message)
+        }
+    })
+
+    const form = useForm({
+        defaultValues: {
+            glucose: 0,
+            type: 'before-meal',
+            date: new Date().toISOString(),
+            units: 'imperial',
+            notes: '',
+        },
+        validators: {
+            onChange: glucoseFormSchema
+        },
+        onSubmit: ({ value }) => {
+            createGlucoseLog({
+                ...value,
+                type: value.type as "before-meal" | "after-meal" | "bedtime" | "fasting" | "random",
+                units: value.units as "imperial" | "metric",
+            })
+        }
+    })
+
+    return (
+        <div className='mt-8'>
+            <form onSubmit={(e) => {
+                e.preventDefault()
+                e.stopPropagation()
+
+                form.handleSubmit()
+            }} className="grid gap-4 mt-3">
+                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+                    <div className="grid gap-2">
+                        <Label htmlFor="glucose">Glucose</Label>
+                        <form.Field
+                            name="glucose"
+                            children={(field) => (
+                                <>
+                                    <Input
+                                        id="glucose"
+                                        placeholder="100"
+                                        value={field.state.value}
+                                        onBlur={field.handleBlur}
+                                        onChange={(e) => field.handleChange(parseInt(e.target.value))}
+                                    />
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="type">Type</Label>
+                        <form.Field
+                            name="type"
+                            children={(field) => (
+                                <>
+                                    <Select
+                                        value={field.state.value}
+                                        defaultValue={field.state.value}
+                                        onValueChange={(e) => field.handleChange(e)}
+                                    >
+                                        <SelectTrigger className='w-full'>
+                                            <SelectValue placeholder="Type" />
+                                        </SelectTrigger>
+                                        <SelectContent>
+                                            {['before-meal', 'after-meal', 'bedtime', 'fasting', 'random'].map((type) => (
+                                                <SelectItem key={type} value={type}>
+                                                    {capitalize(type)}
+                                                </SelectItem>
+                                            ))}
+                                        </SelectContent>
+                                    </Select>
+
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="units">Units</Label>
+                        <form.Field
+                            name="units"
+                            children={(field) => (
+                                <>
+                                    <Select
+                                        value={field.state.value}
+                                        defaultValue={field.state.value}
+                                        onValueChange={(e) => field.handleChange(e)}
+                                    >
+                                        <SelectTrigger className='w-full'>
+                                            <SelectValue placeholder="Units" />
+                                        </SelectTrigger>
+                                        <SelectContent>
+                                            {['imperial', 'metric'].map((type) => (
+                                                <SelectItem key={type} value={type}>
+                                                    {capitalize(type)} - {type === 'imperial' ? 'mg/dL' : 'mmol/L'}
+                                                </SelectItem>
+                                            ))}
+                                        </SelectContent>
+                                    </Select>
path: src/components/logs/forms/glucose.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Add unit conversion support for glucose readings.**

The form allows switching between imperial (mg/dL) and metric (mmol/L) units, but doesn't automatically convert the glucose value. Consider adding unit conversion when the user switches between units.

```diff
 <Select
     value={field.state.value}
     defaultValue={field.state.value}
-    onValueChange={(e) => field.handleChange(e)}
+    onValueChange={(e) => {
+        field.handleChange(e);
+        // Convert glucose value if units changed
+        const glucoseField = form.getFieldValue('glucose');
+        const glucoseValue = glucoseField.state.value;
+        if (typeof glucoseValue === 'number' && !isNaN(glucoseValue)) {
+            if (e === 'metric' && field.state.value === 'imperial') {
+                // Convert from mg/dL to mmol/L: divide by 18
+                form.setFieldValue('glucose', Math.round((glucoseValue / 18) * 10) / 10);
+            } else if (e === 'imperial' && field.state.value === 'metric') {
+                // Convert from mmol/L to mg/dL: multiply by 18
+                form.setFieldValue('glucose', Math.round(glucoseValue * 18));
+            }
+        }
+    }}
 >
```


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,249 @@
+"use client"
+
+import { useForm } from '@tanstack/react-form'
+import dayjs from 'dayjs'
+import { CalendarIcon, Loader2 } from 'lucide-react'
+import { toast } from 'sonner'
+import { Button } from '~/components/ui/button'
+import { Calendar } from '~/components/ui/calendar'
+import { Input } from '~/components/ui/input'
+import { Label } from '~/components/ui/label'
+import { Popover, PopoverContent, PopoverTrigger } from '~/components/ui/popover'
+import {
+    Select,
+    SelectContent,
+    SelectItem,
+    SelectTrigger,
+    SelectValue,
+} from "~/components/ui/select"
+import { Textarea } from '~/components/ui/textarea'
+import { TimePickerDemo } from '~/components/ui/time-picker'
+import { toISOStringWithTimezone } from '~/lib/time-picker-utils'
+import { capitalize, cn } from '~/lib/utils'
+import { glucoseFormSchema } from '~/schemas/logs'
+import { api } from '~/trpc/react'
+
+const GlucoseForm = () => {
+    const { mutate: createGlucoseLog, isPending: isGlucoseLogPending } = api.glucose.create.useMutation({
+        onSuccess: () => {
+            form.reset()
+
+            toast.success("Glucose log created successfully")
+        },
+        onError: (error) => {
+            toast.error(error.message)
+        }
+    })
+
+    const form = useForm({
+        defaultValues: {
+            glucose: 0,
+            type: 'before-meal',
+            date: new Date().toISOString(),
+            units: 'imperial',
+            notes: '',
+        },
+        validators: {
+            onChange: glucoseFormSchema
+        },
+        onSubmit: ({ value }) => {
+            createGlucoseLog({
+                ...value,
+                type: value.type as "before-meal" | "after-meal" | "bedtime" | "fasting" | "random",
+                units: value.units as "imperial" | "metric",
+            })
+        }
+    })
+
+    return (
+        <div className='mt-8'>
+            <form onSubmit={(e) => {
+                e.preventDefault()
+                e.stopPropagation()
+
+                form.handleSubmit()
+            }} className="grid gap-4 mt-3">
+                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+                    <div className="grid gap-2">
+                        <Label htmlFor="glucose">Glucose</Label>
+                        <form.Field
+                            name="glucose"
+                            children={(field) => (
+                                <>
+                                    <Input
+                                        id="glucose"
+                                        placeholder="100"
+                                        value={field.state.value}
+                                        onBlur={field.handleBlur}
+                                        onChange={(e) => field.handleChange(parseInt(e.target.value))}
+                                    />
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
path: src/components/logs/forms/glucose.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix input parsing to handle empty input and prevent NaN values.**

The current implementation will parse an empty input to `NaN` when using `parseInt`. This could lead to validation errors or unexpected behavior.

```diff
 <Input
     id="glucose"
     placeholder="100"
     value={field.state.value}
     onBlur={field.handleBlur}
-    onChange={(e) => field.handleChange(parseInt(e.target.value))}
+    onChange={(e) => {
+        const value = e.target.value.trim();
+        field.handleChange(value === '' ? '' : Number.parseInt(value, 10));
+    }}
 />
```


<!-- 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
                                    <Input
                                        id="glucose"
                                        placeholder="100"
                                        value={field.state.value}
                                        onBlur={field.handleBlur}
                                        onChange={(e) => {
                                            const value = e.target.value.trim();
                                            field.handleChange(
                                                value === '' ? '' : Number.parseInt(value, 10)
                                            );
                                        }}
                                    />
`````

</details>

<!-- suggestion_end -->

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

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

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

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

(lint/correctness/noChildrenProp)

---

[error] 78-78: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 81-81: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation. 

(lint/suspicious/noArrayIndexKey)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,48 @@
+"use client"
+
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "~/lib/utils"
+
+function Popover({
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
+  return <PopoverPrimitive.Root data-slot="popover" {...props} />
+}
+
+function PopoverTrigger({
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
+  return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
+}
+
+function PopoverContent({
+  className,
+  align = "center",
+  sideOffset = 4,
+  ...props
+}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
+  return (
+    <PopoverPrimitive.Portal>
+      <PopoverPrimitive.Content
+        data-slot="popover-content"
+        align={align}
+        sideOffset={sideOffset}
+        className={cn(
+          "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
+          className
+        )}
+        {...props}
+      />
+    </PopoverPrimitive.Portal>
+  )
+}
path: src/components/ui/popover.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix CSS class name in PopoverContent component.**

There's an issue with the CSS origin class using an incorrect syntax.

```diff
 className={cn(
-  "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
+  "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-md border p-4 shadow-md outline-hidden",
   className
 )}
```


<!-- 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
function PopoverContent({
  className,
  align = "center",
  sideOffset = 4,
  ...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
  return (
    <PopoverPrimitive.Portal>
      <PopoverPrimitive.Content
        data-slot="popover-content"
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-[var(--radix-popover-content-transform-origin)] rounded-md border p-4 shadow-md outline-hidden",
          className
        )}
        {...props}
      />
    </PopoverPrimitive.Portal>
  )
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,222 @@
+/**
+ * regular expression to check for valid hour format (01-23)
+ */
+export function isValidHour(value: string) {
+  return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid 12 hour format (01-12)
+ */
+export function isValid12Hour(value: string) {
+  return /^(0[1-9]|1[0-2])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid minute format (00-59)
+ */
+export function isValidMinuteOrSecond(value: string) {
+  return /^[0-5][0-9]$/.test(value);
+}
+
+type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
+
+export function getValidNumber(
+  value: string,
+  { max, min = 0, loop = false }: GetValidNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+
+  if (!isNaN(numericValue)) {
+    if (!loop) {
+      if (numericValue > max) numericValue = max;
+      if (numericValue < min) numericValue = min;
+    } else {
+      if (numericValue > max) numericValue = min;
+      if (numericValue < min) numericValue = max;
+    }
+    return numericValue.toString().padStart(2, "0");
+  }
+
+  return "00";
+}
+
+export function getValidHour(value: string) {
+  if (isValidHour(value)) return value;
+  return getValidNumber(value, { max: 23 });
+}
+
+export function getValid12Hour(value: string) {
+  if (isValid12Hour(value)) return value;
+  return getValidNumber(value, { min: 1, max: 12 });
+}
+
+export function getValidMinuteOrSecond(value: string) {
+  if (isValidMinuteOrSecond(value)) return value;
+  return getValidNumber(value, { max: 59 });
+}
+
+type GetValidArrowNumberConfig = {
+  min: number;
+  max: number;
+  step: number;
+};
+
+export function getValidArrowNumber(
+  value: string,
+  { min, max, step }: GetValidArrowNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+  if (!isNaN(numericValue)) {
+    numericValue += step;
+    return getValidNumber(String(numericValue), { min, max, loop: true });
+  }
+  return "00";
+}
+
+export function getValidArrowHour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 23, step });
+}
+
+export function getValidArrow12Hour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 1, max: 12, step });
+}
+
+export function getValidArrowMinuteOrSecond(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 59, step });
+}
path: src/lib/time-picker-utils.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Use `Number` namespace methods in arrow number handling.**

As with the previous functions, replace global `parseInt` and `isNaN` with their `Number` namespace equivalents.

```diff
export function getValidArrowNumber(
  value: string,
  { min, max, step }: GetValidArrowNumberConfig
) {
-  let numericValue = parseInt(value, 10);
-  if (!isNaN(numericValue)) {
+  let numericValue = Number.parseInt(value, 10);
+  if (!Number.isNaN(numericValue)) {
    numericValue += step;
    return getValidNumber(String(numericValue), { min, max, loop: true });
  }
  return "00";
}
```


<!-- 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
export function getValidArrowNumber(
  value: string,
  { min, max, step }: GetValidArrowNumberConfig
) {
  let numericValue = Number.parseInt(value, 10);
  if (!Number.isNaN(numericValue)) {
    numericValue += step;
    return getValidNumber(String(numericValue), { min, max, loop: true });
  }
  return "00";
}
`````

</details>

<!-- suggestion_end -->

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

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

[error] 69-69: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 70-70: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.


(lint/suspicious/noGlobalIsNan)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,222 @@
+/**
+ * regular expression to check for valid hour format (01-23)
+ */
+export function isValidHour(value: string) {
+  return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid 12 hour format (01-12)
+ */
+export function isValid12Hour(value: string) {
+  return /^(0[1-9]|1[0-2])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid minute format (00-59)
+ */
+export function isValidMinuteOrSecond(value: string) {
+  return /^[0-5][0-9]$/.test(value);
+}
+
+type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
+
+export function getValidNumber(
+  value: string,
+  { max, min = 0, loop = false }: GetValidNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+
+  if (!isNaN(numericValue)) {
+    if (!loop) {
+      if (numericValue > max) numericValue = max;
+      if (numericValue < min) numericValue = min;
+    } else {
+      if (numericValue > max) numericValue = min;
+      if (numericValue < min) numericValue = max;
+    }
+    return numericValue.toString().padStart(2, "0");
+  }
+
+  return "00";
+}
+
+export function getValidHour(value: string) {
+  if (isValidHour(value)) return value;
+  return getValidNumber(value, { max: 23 });
+}
+
+export function getValid12Hour(value: string) {
+  if (isValid12Hour(value)) return value;
+  return getValidNumber(value, { min: 1, max: 12 });
+}
+
+export function getValidMinuteOrSecond(value: string) {
+  if (isValidMinuteOrSecond(value)) return value;
+  return getValidNumber(value, { max: 59 });
+}
path: src/lib/time-picker-utils.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Use `Number` namespace methods for better type safety.**

Replace global `parseInt` and `isNaN` with their `Number` namespace equivalents for better type safety and to follow modern JavaScript best practices.

```diff
export function getValidNumber(
  value: string,
  { max, min = 0, loop = false }: GetValidNumberConfig
) {
-  let numericValue = parseInt(value, 10);
+  let numericValue = Number.parseInt(value, 10);

-  if (!isNaN(numericValue)) {
+  if (!Number.isNaN(numericValue)) {
    if (!loop) {
      if (numericValue > max) numericValue = max;
      if (numericValue < min) numericValue = min;
    } else {
      if (numericValue > max) numericValue = min;
      if (numericValue < min) numericValue = max;
    }
    return numericValue.toString().padStart(2, "0");
  }

  return "00";
}
```


<!-- 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
export function getValidNumber(
  value: string,
  { max, min = 0, loop = false }: GetValidNumberConfig
) {
  let numericValue = Number.parseInt(value, 10);

  if (!Number.isNaN(numericValue)) {
    if (!loop) {
      if (numericValue > max) numericValue = max;
      if (numericValue < min) numericValue = min;
    } else {
      if (numericValue > max) numericValue = min;
      if (numericValue < min) numericValue = max;
    }
    return numericValue.toString().padStart(2, "0");
  }

  return "00";
}
`````

</details>

<!-- suggestion_end -->

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

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

[error] 28-28: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 30-30: isNaN is unsafe. It attempts a type coercion. Use Number.isNaN instead.

See the MDN documentation for more details.
Unsafe fix: Use Number.isNaN instead.


(lint/suspicious/noGlobalIsNan)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:58Z
author_association: NONE
diff_hunk: @@ -0,0 +1,222 @@
+/**
+ * regular expression to check for valid hour format (01-23)
+ */
+export function isValidHour(value: string) {
+  return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid 12 hour format (01-12)
+ */
+export function isValid12Hour(value: string) {
+  return /^(0[1-9]|1[0-2])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid minute format (00-59)
+ */
+export function isValidMinuteOrSecond(value: string) {
+  return /^[0-5][0-9]$/.test(value);
+}
+
+type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
+
+export function getValidNumber(
+  value: string,
+  { max, min = 0, loop = false }: GetValidNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+
+  if (!isNaN(numericValue)) {
+    if (!loop) {
+      if (numericValue > max) numericValue = max;
+      if (numericValue < min) numericValue = min;
+    } else {
+      if (numericValue > max) numericValue = min;
+      if (numericValue < min) numericValue = max;
+    }
+    return numericValue.toString().padStart(2, "0");
+  }
+
+  return "00";
+}
+
+export function getValidHour(value: string) {
+  if (isValidHour(value)) return value;
+  return getValidNumber(value, { max: 23 });
+}
+
+export function getValid12Hour(value: string) {
+  if (isValid12Hour(value)) return value;
+  return getValidNumber(value, { min: 1, max: 12 });
+}
+
+export function getValidMinuteOrSecond(value: string) {
+  if (isValidMinuteOrSecond(value)) return value;
+  return getValidNumber(value, { max: 59 });
+}
+
+type GetValidArrowNumberConfig = {
+  min: number;
+  max: number;
+  step: number;
+};
+
+export function getValidArrowNumber(
+  value: string,
+  { min, max, step }: GetValidArrowNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+  if (!isNaN(numericValue)) {
+    numericValue += step;
+    return getValidNumber(String(numericValue), { min, max, loop: true });
+  }
+  return "00";
+}
+
+export function getValidArrowHour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 23, step });
+}
+
+export function getValidArrow12Hour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 1, max: 12, step });
+}
+
+export function getValidArrowMinuteOrSecond(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 59, step });
+}
+
+export function setMinutes(date: Date, value: string) {
+  const minutes = getValidMinuteOrSecond(value);
+  date.setMinutes(parseInt(minutes, 10));
+  return date;
+}
+
+export function setSeconds(date: Date, value: string) {
+  const seconds = getValidMinuteOrSecond(value);
+  date.setSeconds(parseInt(seconds, 10));
+  return date;
+}
+
+export function setHours(date: Date, value: string) {
+  const hours = getValidHour(value);
+  date.setHours(parseInt(hours, 10));
+  return date;
+}
+
+export function set12Hours(date: Date, value: string, period: Period) {
+  const hours = parseInt(getValid12Hour(value), 10);
+  const convertedHours = convert12HourTo24Hour(hours, period);
+  date.setHours(convertedHours);
+  return date;
+}
path: src/lib/time-picker-utils.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Update Date setter functions to use `Number.parseInt`.**

For consistency, update all `parseInt` calls to use `Number.parseInt`.

```diff
export function setMinutes(date: Date, value: string) {
  const minutes = getValidMinuteOrSecond(value);
-  date.setMinutes(parseInt(minutes, 10));
+  date.setMinutes(Number.parseInt(minutes, 10));
  return date;
}

export function setSeconds(date: Date, value: string) {
  const seconds = getValidMinuteOrSecond(value);
-  date.setSeconds(parseInt(seconds, 10));
+  date.setSeconds(Number.parseInt(seconds, 10));
  return date;
}

export function setHours(date: Date, value: string) {
  const hours = getValidHour(value);
-  date.setHours(parseInt(hours, 10));
+  date.setHours(Number.parseInt(hours, 10));
  return date;
}

export function set12Hours(date: Date, value: string, period: Period) {
-  const hours = parseInt(getValid12Hour(value), 10);
+  const hours = Number.parseInt(getValid12Hour(value), 10);
  const convertedHours = convert12HourTo24Hour(hours, period);
  date.setHours(convertedHours);
  return 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
export function setMinutes(date: Date, value: string) {
  const minutes = getValidMinuteOrSecond(value);
  date.setMinutes(Number.parseInt(minutes, 10));
  return date;
}

export function setSeconds(date: Date, value: string) {
  const seconds = getValidMinuteOrSecond(value);
  date.setSeconds(Number.parseInt(seconds, 10));
  return date;
}

export function setHours(date: Date, value: string) {
  const hours = getValidHour(value);
  date.setHours(Number.parseInt(hours, 10));
  return date;
}

export function set12Hours(date: Date, value: string, period: Period) {
  const hours = Number.parseInt(getValid12Hour(value), 10);
  const convertedHours = convert12HourTo24Hour(hours, period);
  date.setHours(convertedHours);
  return date;
}
`````

</details>

<!-- suggestion_end -->

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

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

[error] 91-91: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 97-97: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 103-103: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

---

[error] 108-108: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.


(lint/style/useNumberNamespace)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,222 @@
+/**
+ * regular expression to check for valid hour format (01-23)
+ */
+export function isValidHour(value: string) {
+  return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid 12 hour format (01-12)
+ */
+export function isValid12Hour(value: string) {
+  return /^(0[1-9]|1[0-2])$/.test(value);
+}
+
+/**
+ * regular expression to check for valid minute format (00-59)
+ */
+export function isValidMinuteOrSecond(value: string) {
+  return /^[0-5][0-9]$/.test(value);
+}
+
+type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
+
+export function getValidNumber(
+  value: string,
+  { max, min = 0, loop = false }: GetValidNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+
+  if (!isNaN(numericValue)) {
+    if (!loop) {
+      if (numericValue > max) numericValue = max;
+      if (numericValue < min) numericValue = min;
+    } else {
+      if (numericValue > max) numericValue = min;
+      if (numericValue < min) numericValue = max;
+    }
+    return numericValue.toString().padStart(2, "0");
+  }
+
+  return "00";
+}
+
+export function getValidHour(value: string) {
+  if (isValidHour(value)) return value;
+  return getValidNumber(value, { max: 23 });
+}
+
+export function getValid12Hour(value: string) {
+  if (isValid12Hour(value)) return value;
+  return getValidNumber(value, { min: 1, max: 12 });
+}
+
+export function getValidMinuteOrSecond(value: string) {
+  if (isValidMinuteOrSecond(value)) return value;
+  return getValidNumber(value, { max: 59 });
+}
+
+type GetValidArrowNumberConfig = {
+  min: number;
+  max: number;
+  step: number;
+};
+
+export function getValidArrowNumber(
+  value: string,
+  { min, max, step }: GetValidArrowNumberConfig
+) {
+  let numericValue = parseInt(value, 10);
+  if (!isNaN(numericValue)) {
+    numericValue += step;
+    return getValidNumber(String(numericValue), { min, max, loop: true });
+  }
+  return "00";
+}
+
+export function getValidArrowHour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 23, step });
+}
+
+export function getValidArrow12Hour(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 1, max: 12, step });
+}
+
+export function getValidArrowMinuteOrSecond(value: string, step: number) {
+  return getValidArrowNumber(value, { min: 0, max: 59, step });
+}
+
+export function setMinutes(date: Date, value: string) {
+  const minutes = getValidMinuteOrSecond(value);
+  date.setMinutes(parseInt(minutes, 10));
+  return date;
+}
+
+export function setSeconds(date: Date, value: string) {
+  const seconds = getValidMinuteOrSecond(value);
+  date.setSeconds(parseInt(seconds, 10));
+  return date;
+}
+
+export function setHours(date: Date, value: string) {
+  const hours = getValidHour(value);
+  date.setHours(parseInt(hours, 10));
+  return date;
+}
+
+export function set12Hours(date: Date, value: string, period: Period) {
+  const hours = parseInt(getValid12Hour(value), 10);
+  const convertedHours = convert12HourTo24Hour(hours, period);
+  date.setHours(convertedHours);
+  return date;
+}
+
+export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
+export type Period = "AM" | "PM";
+
+export function setDateByType(
+  date: Date,
+  value: string,
+  type: TimePickerType,
+  period?: Period
+) {
+  switch (type) {
+    case "minutes":
+      return setMinutes(date, value);
+    case "seconds":
+      return setSeconds(date, value);
+    case "hours":
+      return setHours(date, value);
+    case "12hours": {
+      if (!period) return date;
+      return set12Hours(date, value, period);
+    }
+    default:
+      return date;
+  }
+}
+
+export function getDateByType(date: Date, type: TimePickerType) {
+  switch (type) {
+    case "minutes":
+      return getValidMinuteOrSecond(String(date.getMinutes()));
+    case "seconds":
+      return getValidMinuteOrSecond(String(date.getSeconds()));
+    case "hours":
+      return getValidHour(String(date.getHours()));
+    case "12hours":
+      const hours = display12HourValue(date.getHours());
+      return getValid12Hour(String(hours));
+    default:
+      return "00";
+  }
+}
path: src/lib/time-picker-utils.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix switch case variable scope issue.**

Wrap the hours variable declaration in a block to restrict its access to the switch clause and prevent potential issues.

```diff
export function getDateByType(date: Date, type: TimePickerType) {
  switch (type) {
    case "minutes":
      return getValidMinuteOrSecond(String(date.getMinutes()));
    case "seconds":
      return getValidMinuteOrSecond(String(date.getSeconds()));
    case "hours":
      return getValidHour(String(date.getHours()));
    case "12hours": {
      const hours = display12HourValue(date.getHours());
      return getValid12Hour(String(hours));
+    }
    default:
      return "00";
  }
}
```


<!-- 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
export function getDateByType(date: Date, type: TimePickerType) {
  switch (type) {
    case "minutes":
      return getValidMinuteOrSecond(String(date.getMinutes()));
    case "seconds":
      return getValidMinuteOrSecond(String(date.getSeconds()));
    case "hours":
      return getValidHour(String(date.getHours()));
    case "12hours": {
      const hours = display12HourValue(date.getHours());
      return getValid12Hour(String(hours));
    }
    default:
      return "00";
  }
}
`````

</details>

<!-- suggestion_end -->

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

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

[error] 148-148: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.


(lint/correctness/noSwitchDeclarations)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T20:40:59Z
author_association: NONE
diff_hunk: @@ -0,0 +1,250 @@
+"use client"
+
+import { useForm } from '@tanstack/react-form'
+import dayjs from 'dayjs'
+import { CalendarIcon, Loader2 } from 'lucide-react'
+import { toast } from 'sonner'
+import { Button } from '~/components/ui/button'
+import { Calendar } from '~/components/ui/calendar'
+import { Input } from '~/components/ui/input'
+import { Label } from '~/components/ui/label'
+import { Popover, PopoverContent, PopoverTrigger } from '~/components/ui/popover'
+import {
+    Select,
+    SelectContent,
+    SelectItem,
+    SelectTrigger,
+    SelectValue,
+} from "~/components/ui/select"
+import { Textarea } from '~/components/ui/textarea'
+import { TimePicker } from '~/components/ui/time-picker'
+import { toISOStringWithTimezone } from '~/lib/time-picker-utils'
+import { capitalize, cn } from '~/lib/utils'
+import { glucoseFormSchema } from '~/schemas/logs'
+import { api } from '~/trpc/react'
+
+const GlucoseForm = () => {
+    const { mutate: createGlucoseLog, isPending: isGlucoseLogPending } = api.glucose.create.useMutation({
+        onSuccess: () => {
+            form.reset()
+
+            toast.success("Glucose log created successfully")
+        },
+        onError: (error) => {
+            toast.error(error.message)
+        }
+    })
+
+    const form = useForm({
+        defaultValues: {
+            glucose: 0,
+            type: 'before-meal',
+            date: new Date().toISOString(),
+            units: 'imperial',
+            notes: '',
+        },
+        validators: {
+            onChange: glucoseFormSchema
+        },
+        onSubmit: ({ value }) => {
+            createGlucoseLog({
+                ...value,
+                type: value.type as "before-meal" | "after-meal" | "bedtime" | "fasting" | "random",
+                units: value.units as "imperial" | "metric",
+            })
+        }
+    })
+
+    return (
+        <div className='mt-8'>
+            <form onSubmit={(e) => {
+                e.preventDefault()
+                e.stopPropagation()
+
+                form.handleSubmit()
+            }} className="grid gap-4 mt-3">
+                <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+                    <div className="grid gap-2">
+                        <Label htmlFor="glucose">Glucose</Label>
+                        <form.Field
+                            name="glucose"
+                            children={(field) => (
+                                <>
+                                    <Input
+                                        id="glucose"
+                                        type="number"
+                                        placeholder="100"
+                                        value={field.state.value}
+                                        onBlur={field.handleBlur}
+                                        onChange={(e) => field.handleChange(parseInt(e.target.value))}
+                                    />
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="type">Type</Label>
+                        <form.Field
+                            name="type"
+                            children={(field) => (
+                                <>
+                                    <Select
+                                        value={field.state.value}
+                                        defaultValue={field.state.value}
+                                        onValueChange={(e) => field.handleChange(e)}
+                                    >
+                                        <SelectTrigger className='w-full'>
+                                            <SelectValue placeholder="Type" />
+                                        </SelectTrigger>
+                                        <SelectContent>
+                                            {['before-meal', 'after-meal', 'bedtime', 'fasting', 'random'].map((type) => (
+                                                <SelectItem key={type} value={type}>
+                                                    {capitalize(type)}
+                                                </SelectItem>
+                                            ))}
+                                        </SelectContent>
+                                    </Select>
+
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="units">Units</Label>
+                        <form.Field
+                            name="units"
+                            children={(field) => (
+                                <>
+                                    <Select
+                                        value={field.state.value}
+                                        defaultValue={field.state.value}
+                                        onValueChange={(e) => field.handleChange(e)}
+                                    >
+                                        <SelectTrigger className='w-full'>
+                                            <SelectValue placeholder="Units" />
+                                        </SelectTrigger>
+                                        <SelectContent>
+                                            {['imperial', 'metric'].map((type) => (
+                                                <SelectItem key={type} value={type}>
+                                                    {capitalize(type)} - {type === 'imperial' ? 'mg/dL' : 'mmol/L'}
+                                                </SelectItem>
+                                            ))}
+                                        </SelectContent>
+                                    </Select>
+
+                                    {field.state.meta.errors.map((error, i) => (
+                                        <div key={i} className="text-red-500 text-sm">
+                                            {error?.message}
+                                        </div>
+                                    ))}
+                                </>
+                            )}
+                        />
+                    </div>
+                    <div className="grid gap-2">
+                        <Label htmlFor="date">Date</Label>
+                        <form.Field
+                            name="date"
+                            children={(field) => {
+                                return (
+                                    <>
+                                        <Popover>
+                                            <PopoverTrigger asChild>
+                                                <Button
+                                                    variant="outline"
+                                                    className={cn(
+                                                        "w-full md:w-[280px] justify-start text-left font-normal",
+                                                        !field.state.value && "text-muted-foreground"
+                                                    )}
+                                                >
+                                                    <CalendarIcon className="mr-2 h-4 w-4" />
+                                                    {field.state.value ? (
+                                                        dayjs(field.state.value).format("DD MMM YYYY HH:mm")
+                                                    ) : (
+                                                        <span>Pick a date</span>
+                                                    )}
+                                                </Button>
+                                            </PopoverTrigger>
+                                            <PopoverContent className="w-auto p-0">
+                                                <Calendar
+                                                    mode="single"
+                                                    selected={field.state.value ? new Date(field.state.value) : undefined}
+                                                    onSelect={(date) => field.handleChange(toISOStringWithTimezone(date ?? new Date()))}
+                                                    initialFocus
+                                                    disabled={(date) => date > new Date()}
+                                                />
+                                                <div className="p-3 border-t border-border">
+                                                    <TimePicker
+                                                        setDate={(date) => field.handleChange(toISOStringWithTimezone(date ?? new Date()))}
+                                                        date={new Date(field.state.value)}
+                                                    />
path: src/components/logs/forms/glucose.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Ensure the time picker maintains the selected date.**

When setting a new time, make sure to preserve the existing date if one is already selected.

```diff
<TimePicker
-    setDate={(date) => field.handleChange(toISOStringWithTimezone(date ?? new Date()))}
-    date={new Date(field.state.value)}
+    setDate={(date) => {
+        if (!date) return;
+        field.handleChange(toISOStringWithTimezone(date));
+    }}
+    date={field.state.value ? new Date(field.state.value) : new 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
<TimePicker
    setDate={(date) => {
        if (!date) return;
        field.handleChange(toISOStringWithTimezone(date));
    }}
    date={field.state.value ? new Date(field.state.value) : new Date()}
/>
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-11T21:09:07Z
author_association: NONE

PR comments:
user: login: vercel[bot]
created_at: 2025-05-11T20:36:54Z
body: [vc]: #ZPXsLe2qPBRPkoKh53JIS3qcb0sb2hxURxPstpvXr3U=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ0My1nbHVjb3RyYWNrZXIiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vamptYmViYXMtcHJvamVjdHMvdDMtZ2x1Y290cmFja2VyL0dMa3JacldtY1RBUXVFeThMcDZjaUFXdGRhNUMiLCJwcmV2aWV3VXJsIjoidDMtZ2x1Y290cmFja2VyLWdpdC1pbXBsZW1lbnQtZ2x1Y29zZS1jODE5MzItamptYmViYXMtcHJvamVjdHMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ0My1nbHVjb3RyYWNrZXItZ2l0LWltcGxlbWVudC1nbHVjb3NlLWM4MTkzMi1qam1iZWJhcy1wcm9qZWN0cy52ZXJjZWwuYXBwIn0sInJvb3REaXJlY3RvcnkiOm51bGx9XX0=
**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/GLkrZrWmcTAQuEy8Lp6ciAWtda5C)) | [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 11, 2025 9:07pm |


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

## Walkthrough

This update introduces a comprehensive glucose logging feature. It adds new UI components for glucose log entry and display, including forms, calendar, time picker, popover, select, scroll area, and textarea components. Supporting backend infrastructure is implemented with API routers, authentication middleware, schema validation, and a new database table for storing glucose logs. Several utility libraries and Radix UI packages are added as dependencies. Minor improvements include sidebar URL updates, session loading UI enhancements, and removal of debugging logs.

## Changes

| File(s) / Path(s)                                                              | Change Summary |
|--------------------------------------------------------------------------------|---------------|
| `package.json`                                                                 | Added dependencies: `@radix-ui/react-popover`, `@radix-ui/react-scroll-area`, `@radix-ui/react-select`, `date-fns`, `dayjs`, `react-day-picker`. |
| `src/app/(home)/logs/glucose/page.tsx`                                         | Added new page with tabbed interface for glucose logs: history and upload, using new `GlucoseForm`. |
| `src/components/logs/forms/glucose.tsx`                                        | Added `GlucoseForm` React component for glucose log entry with validation, date/time picker, and mutation logic. |
| `src/components/ui/calendar.tsx`                                               | Added `Calendar` component wrapping `react-day-picker` with custom styling and navigation. |
| `src/components/ui/popover.tsx`                                                | Added popover UI components (`Popover`, `PopoverTrigger`, `PopoverContent`, `PopoverAnchor`) using Radix primitives. |
| `src/components/ui/scroll-area.tsx`                                            | Added `ScrollArea` and `ScrollBar` components wrapping Radix primitives for custom scroll areas. |
| `src/components/ui/select.tsx`                                                 | Added customizable `Select` dropdown components built on Radix UI, with trigger, content, item, label, and scroll buttons. |
| `src/components/ui/textarea.tsx`                                               | Added styled `Textarea` component with standard HTML attributes and utility classes. |
| `src/components/ui/time-picker-input.tsx`, `src/components/ui/time-picker.tsx` | Added time picker input and composite component for hour/minute selection with keyboard navigation. |
| `src/lib/time-picker-utils.ts`                                                 | Added utility functions for time validation, formatting, conversion, and manipulation for time pickers. |
| `src/schemas/logs.ts`                                                          | Added `glucoseFormSchema` for validating glucose log form data using `zod`. |
| `src/server/api/root.ts`                                                       | Added `glucoseRouter` to main API router. |
| `src/server/api/routers/glucose.ts`                                            | Added `glucoseRouter` with a protected `create` mutation for inserting glucose logs. |
| `src/server/api/trpc.ts`                                                       | Added authentication to tRPC context, `isAuthed` middleware, and `protectedProcedure` for secure endpoints. |
| `src/server/db/schema.ts`                                                      | Added `glucoseLog` table schema for storing glucose log records. |
| `src/components/app-sidebar.tsx`                                               | Updated sidebar links for logs to new URL structure without `/dashboard` prefix. |
| `src/components/auth/forms/sign-up.tsx`                                        | Removed unused `data` destructuring from `useSession()` in sign-up form. |
| `src/components/common/user-button.tsx`                                        | Improved session loading state with skeleton UI and renamed loading state variable. |
| `src/app/api/uploadthing/core.ts`, `src/components/profile/forms/profle-image.tsx` | Removed unnecessary or debugging `console.log` statements. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant GlucoseLogsPage
    participant GlucoseForm
    participant API (tRPC)
    participant DB

    User->>GlucoseLogsPage: Navigates to /logs/glucose
    GlucoseLogsPage->>GlucoseForm: Renders Upload tab
    User->>GlucoseForm: Enters glucose data and submits
    GlucoseForm->>API (tRPC): Calls glucose.create mutation with form data
    API (tRPC)->>DB: Inserts glucose log (with userId, date, value, etc.)
    DB-->>API (tRPC): Confirms insertion
    API (tRPC)-->>GlucoseForm: Returns success message
    GlucoseForm-->>User: Shows toast notification and resets form
```

## Poem

> A bunny with a clever nose,  
> Hopped through forms where glucose flows.  
> With popovers, selects, and times to pick,  
> Logging each value with a gentle click.  
> New tables and routers behind the scenes,  
> Make tracking health less routine!  
> 🐇✨

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


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

<!-- 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=6):

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

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