diff --git a/apps/expo/package.json b/apps/expo/package.json
index 4ff5a8d..5260680 100644
--- a/apps/expo/package.json
+++ b/apps/expo/package.json
@@ -42,6 +42,8 @@
"@rn-primitives/label": "^1.0.3",
"@rn-primitives/portal": "^1.0.3",
"@rn-primitives/progress": "^1.0.3",
+ "@rn-primitives/radio-group": "^1.0.3",
+ "@rn-primitives/select": "^1.0.4",
"@rn-primitives/separator": "^1.0.3",
"@rn-primitives/slot": "^1.0.3",
"@rn-primitives/tooltip": "^1.0.3",
diff --git a/apps/expo/src/app/(app)/(tabs)/account.tsx b/apps/expo/src/app/(app)/(tabs)/account.tsx
index 5c95f14..cb8d164 100644
--- a/apps/expo/src/app/(app)/(tabs)/account.tsx
+++ b/apps/expo/src/app/(app)/(tabs)/account.tsx
@@ -8,6 +8,8 @@ import {
import DexcomCGMData from "~/components/dexcom/dexcom-data";
import DexcomDevicesList from "~/components/dexcom/dexcom-devices";
import { DexcomLogin } from "~/components/dexcom/dexcom-login";
+import { ChangeRangeSetting } from "~/components/glucose/change-range-setting";
+import { CalculateRecap } from "~/components/glucose/create-recap";
import { ThemeToggle } from "~/components/theme-toggle";
import { Button } from "~/components/ui/button";
import { Text } from "~/components/ui/text";
@@ -41,10 +43,14 @@ export default function AccountScreen() {
Account Screen
{user?.id && }
+
+
+
+
);
diff --git a/apps/expo/src/components/calendar/home-calendar.tsx b/apps/expo/src/components/calendar/home-calendar.tsx
index 3e86697..30372d4 100644
--- a/apps/expo/src/components/calendar/home-calendar.tsx
+++ b/apps/expo/src/components/calendar/home-calendar.tsx
@@ -5,38 +5,44 @@ import type {
import { useCallback, useMemo, useState } from "react";
import { View } from "react-native";
import { fromDateId, toDateId } from "@marceloterreiro/flash-calendar";
-import { add, endOfMonth, format, startOfMonth, sub } from "date-fns";
+import { add, sub } from "date-fns";
import { format as formatFP } from "date-fns/fp";
import { BasicCalendar } from "~/components/calendar/basic-calendar";
-import { Text } from "~/components/ui/text";
+import { Progress } from "~/components/ui/progress";
import { useDateStore } from "~/stores/date-store";
import { useGlucoseStore } from "~/stores/glucose-store";
import { api } from "~/utils/api";
export function HomeCalendar() {
- const { selectedDate, setSelectedDate, setIsCalendarOpen } = useDateStore();
+ const {
+ selectedDate,
+ setSelectedDate,
+ setIsCalendarOpen,
+ updateVisibleDates,
+ } = useDateStore();
const { rangeView } = useGlucoseStore();
const [currentCalendarMonth, setCurrentCalendarMonth] =
useState(selectedDate);
- const startDate = startOfMonth(selectedDate);
- const endDate = endOfMonth(selectedDate);
-
- const { data: dailyRecaps, isPending } = api.recap.getDailyRecaps.useQuery({
- startDate: format(startDate, "yyyy-MM-dd"),
- endDate: format(endDate, "yyyy-MM-dd"),
- });
+ const { data: allRecaps, isPending } = api.recap.all.useQuery();
const currentDate = new Date();
+ const minDate = allRecaps
+ ? new Date(
+ Math.min(...allRecaps.map((recap) => new Date(recap.date).getTime())),
+ )
+ : new Date("2024-08-01");
const handleDayPress = useCallback(
(dateId) => {
- setCurrentCalendarMonth(fromDateId(dateId));
- setSelectedDate(fromDateId(dateId));
+ const newDate = fromDateId(dateId);
+ setCurrentCalendarMonth(newDate);
+ setSelectedDate(newDate);
+ updateVisibleDates(newDate);
setIsCalendarOpen(false); // Close the dialog after selecting a date
},
- [setSelectedDate, setIsCalendarOpen],
+ [setSelectedDate, setIsCalendarOpen, updateVisibleDates],
);
const calendarActiveDateRanges = useMemo(
@@ -57,16 +63,12 @@ export function HomeCalendar() {
setCurrentCalendarMonth(add(currentCalendarMonth, { months: 1 }));
}, [currentCalendarMonth]);
- if (isPending) {
- return Loading...;
- }
-
return (
+ {isPending && }
);
}
diff --git a/apps/expo/src/components/glucose/change-range-setting.tsx b/apps/expo/src/components/glucose/change-range-setting.tsx
new file mode 100644
index 0000000..27d2b68
--- /dev/null
+++ b/apps/expo/src/components/glucose/change-range-setting.tsx
@@ -0,0 +1,77 @@
+import React from "react";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
+
+import type { GlucoseRangeTypes } from "@hyper/db/schema";
+
+import { Label } from "~/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "~/components/ui/select";
+import { useGlucoseStore } from "~/stores/glucose-store";
+
+interface RangeOption {
+ value: GlucoseRangeTypes;
+ label: string;
+}
+
+function ChangeRangeSetting() {
+ const insets = useSafeAreaInsets();
+ const contentInsets = {
+ top: insets.top,
+ bottom: insets.bottom,
+ left: insets.left,
+ right: insets.right,
+ };
+
+ const { rangeView, setRangeView } = useGlucoseStore();
+
+ console.log(rangeView);
+
+ const capitalizeFirstLetter = (string: string) => {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ };
+
+ const rangeOptions: RangeOption[] = [
+ { value: "standard", label: capitalizeFirstLetter("standard") },
+ { value: "tight", label: capitalizeFirstLetter("tight") },
+ { value: "optimal", label: capitalizeFirstLetter("optimal") },
+ ];
+
+ const currentOption =
+ rangeOptions.find((option) => option.value === rangeView) ??
+ rangeOptions[0];
+
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export { ChangeRangeSetting };
diff --git a/apps/expo/src/components/glucose/create-recap.tsx b/apps/expo/src/components/glucose/create-recap.tsx
new file mode 100644
index 0000000..975216f
--- /dev/null
+++ b/apps/expo/src/components/glucose/create-recap.tsx
@@ -0,0 +1,55 @@
+import React from "react";
+import { Alert, View } from "react-native";
+
+import { Button } from "~/components/ui/button";
+import { Text } from "~/components/ui/text";
+import { api } from "~/utils/api";
+
+const CalculateRecap: React.FC = () => {
+ const utils = api.useUtils();
+
+ const mutation = api.recap.calculateAndStoreRecapsForRange.useMutation({
+ onSuccess: async (data) => {
+ Alert.alert(
+ "Success",
+ `Recaps calculated and stored successfully for ${data.length} days.`,
+ );
+ await utils.recap.getDailyRecaps.invalidate();
+ },
+ onError: (error) => {
+ Alert.alert("Error", `Failed to calculate recaps: ${error.message}`);
+ },
+ });
+
+ const handleCalculateRecaps = () => {
+ const augustStart = new Date("2024-08-01T00:00:00Z");
+ const augustEnd = new Date("2024-08-31T23:59:59Z");
+
+ const queryInput = {
+ startDate: augustStart.toISOString(),
+ endDate: augustEnd.toISOString(),
+ };
+
+ mutation.mutate(queryInput);
+ };
+
+ return (
+
+
+ {mutation.isError ? (
+ An error occurred: {mutation.error.message}
+ ) : null}
+ {mutation.isSuccess ? (
+ Last calculated: {new Date().toLocaleString()}
+ ) : null}
+
+ );
+};
+
+export { CalculateRecap };
diff --git a/apps/expo/src/components/home/day-slider.tsx b/apps/expo/src/components/home/day-slider.tsx
index 16ec181..28f556e 100644
--- a/apps/expo/src/components/home/day-slider.tsx
+++ b/apps/expo/src/components/home/day-slider.tsx
@@ -1,10 +1,11 @@
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import { Dimensions, Pressable, View } from "react-native";
import { FlashList } from "@shopify/flash-list";
-import { endOfDay, format, startOfDay } from "date-fns";
+import { format } from "date-fns";
import type { DailyRecap, GlucoseRangeTypes } from "@hyper/db/schema";
+import { Skeleton } from "~/components/ui/skeleton";
import { Text } from "~/components/ui/text";
import { useColorScheme } from "~/lib/use-color-scheme";
import { cn, getGlucoseRangeColors } from "~/lib/utils";
@@ -19,23 +20,24 @@ const centerOffset = (screenWidth - itemWidth) / 2;
const DayItem = React.memo(
({
- item,
+ date,
+ recap,
isSelected,
onPress,
isDark,
rangeView,
+ isLoading,
}: {
- item: DailyRecap;
+ date: Date;
+ recap?: DailyRecap;
isSelected: boolean;
onPress: () => void;
isDark: boolean;
rangeView: GlucoseRangeTypes;
+ isLoading: boolean;
}) => {
- const date = new Date(item.date);
- const glucoseColors = getGlucoseRangeColors(
- item.timeInRanges?.[rangeView] ?? 0,
- isDark,
- );
+ const timeInRange = recap?.timeInRanges?.[rangeView] ?? 0;
+ const glucoseColors = getGlucoseRangeColors(timeInRange, isDark);
return (
@@ -56,21 +58,25 @@ const DayItem = React.memo(
-
-
+ ) : (
+
- {item.timeInRanges?.[rangeView] ?? "?"}
-
-
+
+ {Math.floor(timeInRange)}
+
+
+ )}
);
@@ -78,30 +84,21 @@ const DayItem = React.memo(
);
export function DaySlider() {
- const { selectedDate, setSelectedDate } = useDateStore();
+ const { selectedDate, setSelectedDate, visibleDates } = useDateStore();
const { rangeView } = useGlucoseStore();
- const listRef = useRef | null>(null);
+ const listRef = useRef | null>(null);
const { colorScheme } = useColorScheme();
const isDark = colorScheme === "dark";
- // Fetch data for the last 30 days
- const endDate = endOfDay(new Date());
- const startDate = startOfDay(
- new Date(endDate.getTime() - 29 * 24 * 60 * 60 * 1000),
- );
-
- const { data: dailyRecaps, isPending } = api.recap.getDailyRecaps.useQuery({
- startDate: startDate.toISOString(),
- endDate: endDate.toISOString(),
- });
+ const { data: allRecaps, isPending } = api.recap.all.useQuery();
- const sortedData = useMemo(() => {
- return dailyRecaps
- ? [...dailyRecaps].sort(
- (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
- )
- : [];
- }, [dailyRecaps]);
+ const recapsMap = useMemo(() => {
+ const map = new Map();
+ allRecaps?.forEach((recap) => {
+ map.set(new Date(recap.date).toDateString(), recap);
+ });
+ return map;
+ }, [allRecaps]);
// const scrollToIndex = useCallback((index: number) => {
// listRef.current?.scrollToIndex({
@@ -120,55 +117,50 @@ export function DaySlider() {
}, []);
const scrollToSelectedDate = useCallback(() => {
- const selectedIndex = sortedData.findIndex(
- (item) =>
- new Date(item.date).toDateString() === selectedDate.toDateString(),
+ const selectedIndex = visibleDates.findIndex(
+ (date) => date.toDateString() === selectedDate.toDateString(),
);
if (selectedIndex !== -1) {
// scrollToIndex(selectedIndex);
scrollToOffset(selectedIndex);
}
- }, [selectedDate, scrollToOffset, sortedData]);
+ }, [selectedDate, scrollToOffset, visibleDates]);
useEffect(() => {
scrollToSelectedDate();
}, [selectedDate, scrollToSelectedDate]);
const renderItem = useCallback(
- ({ item }: { item: DailyRecap }) => {
- const itemDate = new Date(item.date);
- const isSelected =
- itemDate.toDateString() === selectedDate.toDateString();
+ ({ item: date }: { item: Date }) => {
+ const isSelected = date.toDateString() === selectedDate.toDateString();
+ const recap = recapsMap.get(date.toDateString());
+ const isLoading = !recap && isPending;
+
return (
{
- setSelectedDate(itemDate);
+ setSelectedDate(date);
}}
isDark={isDark}
rangeView={rangeView}
+ isLoading={isLoading}
/>
);
},
- [selectedDate, setSelectedDate, isDark, rangeView],
- );
-
- const keyExtractor = useCallback(
- (item: DailyRecap) => item.date.toDateString(),
- [],
+ [selectedDate, setSelectedDate, isDark, rangeView, recapsMap, isPending],
);
- if (isPending) {
- return Loading...;
- }
+ const keyExtractor = useCallback((date: Date) => date.toDateString(), []);
return (
,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+ );
+});
+RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
+
+const RadioGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+
+
+
+
+ );
+});
+RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
+
+export { RadioGroup, RadioGroupItem };
diff --git a/apps/expo/src/components/ui/select.tsx b/apps/expo/src/components/ui/select.tsx
new file mode 100644
index 0000000..61e3812
--- /dev/null
+++ b/apps/expo/src/components/ui/select.tsx
@@ -0,0 +1,199 @@
+import * as React from "react";
+import { Platform, StyleSheet, View } from "react-native";
+import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
+import * as SelectPrimitive from "@rn-primitives/select";
+
+import { Check } from "~/lib/icons/check";
+import { ChevronDown } from "~/lib/icons/chevron-down";
+import { ChevronUp } from "~/lib/icons/chevron-up";
+import { cn } from "~/lib/utils";
+
+type Option = SelectPrimitive.Option;
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ props.disabled && "web:cursor-not-allowed opacity-50",
+ className,
+ )}
+ {...props}
+ >
+ <>{children}>
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+/**
+ * Platform: WEB ONLY
+ */
+const SelectScrollUpButton = ({
+ className,
+ ...props
+}: React.ComponentPropsWithoutRef) => {
+ if (Platform.OS !== "web") {
+ return null;
+ }
+ return (
+
+
+
+ );
+};
+
+/**
+ * Platform: WEB ONLY
+ */
+const SelectScrollDownButton = ({
+ className,
+ ...props
+}: React.ComponentPropsWithoutRef) => {
+ if (Platform.OS !== "web") {
+ return null;
+ }
+ return (
+
+
+
+ );
+};
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ portalHost?: string;
+ }
+>(({ className, children, position = "popper", portalHost, ...props }, ref) => {
+ const { open } = SelectPrimitive.useRootContext();
+
+ return (
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+ );
+});
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+
+
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+ type Option,
+};
diff --git a/apps/expo/src/lib/icons/check.tsx b/apps/expo/src/lib/icons/check.tsx
new file mode 100644
index 0000000..f9cd296
--- /dev/null
+++ b/apps/expo/src/lib/icons/check.tsx
@@ -0,0 +1,6 @@
+import { Check } from "lucide-react-native";
+
+import { iconWithClassName } from "~/lib/icons/icon-with-classname";
+
+iconWithClassName(Check);
+export { Check };
diff --git a/apps/expo/src/lib/icons/chevron-down.tsx b/apps/expo/src/lib/icons/chevron-down.tsx
new file mode 100644
index 0000000..51afc9b
--- /dev/null
+++ b/apps/expo/src/lib/icons/chevron-down.tsx
@@ -0,0 +1,6 @@
+import { ChevronDown } from "lucide-react-native";
+
+import { iconWithClassName } from "~/lib/icons/icon-with-classname";
+
+iconWithClassName(ChevronDown);
+export { ChevronDown };
diff --git a/apps/expo/src/lib/icons/chevron-up.tsx b/apps/expo/src/lib/icons/chevron-up.tsx
new file mode 100644
index 0000000..ba5971e
--- /dev/null
+++ b/apps/expo/src/lib/icons/chevron-up.tsx
@@ -0,0 +1,6 @@
+import { ChevronUp } from "lucide-react-native";
+
+import { iconWithClassName } from "~/lib/icons/icon-with-classname";
+
+iconWithClassName(ChevronUp);
+export { ChevronUp };
diff --git a/apps/expo/src/stores/date-store.ts b/apps/expo/src/stores/date-store.ts
index ce10176..158b9b7 100644
--- a/apps/expo/src/stores/date-store.ts
+++ b/apps/expo/src/stores/date-store.ts
@@ -1,3 +1,4 @@
+import { addDays, isAfter, isBefore, isSameDay, subDays } from "date-fns";
import { create } from "zustand";
interface DateState {
@@ -5,13 +6,42 @@ interface DateState {
setSelectedDate: (date: Date) => void;
isCalendarOpen: boolean;
setIsCalendarOpen: (isOpen: boolean) => void;
+ visibleDates: Date[];
+ updateVisibleDates: (date: Date) => void;
}
+const generateInitialDates = () => {
+ const today = new Date();
+ return Array.from({ length: 30 }, (_, i) => subDays(today, i));
+};
+
+const generateDatesOutsideRange = (centerDate: Date) => {
+ const endDate = addDays(centerDate, 15);
+ return Array.from({ length: 31 }, (_, i) => subDays(endDate, i));
+};
+
export const useDateStore = create((set) => ({
selectedDate: new Date(),
setSelectedDate: (date: Date) => set({ selectedDate: date }),
isCalendarOpen: false,
setIsCalendarOpen: (isOpen: boolean) => set({ isCalendarOpen: isOpen }),
+ visibleDates: generateInitialDates(),
+ updateVisibleDates: (date: Date) =>
+ set(() => {
+ const today = new Date();
+ const thirtyDaysAgo = subDays(today, 30);
+
+ if (
+ isSameDay(date, today) ||
+ (isBefore(date, today) && isAfter(date, thirtyDaysAgo))
+ ) {
+ // Case 1: Date is within the last 30 days or is today
+ return { visibleDates: generateInitialDates() };
+ } else {
+ // Case 2: Date is outside the last 30 days
+ return { visibleDates: generateDatesOutsideRange(date) };
+ }
+ }),
}));
// Example persist-middleware with MMKV
diff --git a/apps/expo/src/stores/glucose-store.ts b/apps/expo/src/stores/glucose-store.ts
index bdab835..a2f9ddf 100644
--- a/apps/expo/src/stores/glucose-store.ts
+++ b/apps/expo/src/stores/glucose-store.ts
@@ -1,16 +1,14 @@
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
-import { GlucoseRangeTypes } from "@hyper/db/schema";
+import type { GlucoseRangeTypes } from "@hyper/db/schema";
import { zustandStorage } from "~/lib/storage";
-type RangeView = "standard" | "tight" | "optimal";
-
interface GlucoseState {
rangeView: GlucoseRangeTypes;
lastSyncedTime: Date | null;
- setRangeView: (view: RangeView) => void;
+ setRangeView: (view: GlucoseRangeTypes) => void;
setLastSyncedTime: (date: Date) => void;
}
diff --git a/package.json b/package.json
index 1ed466b..bd6ba01 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"build": "turbo run build",
"clean": "git clean -xdf node_modules",
"clean:workspaces": "turbo run clean",
+ "daemon:restart": "turbo daemon restart",
"db:generate": "turbo -F @hyper/db dk-generate",
"db:migrate": "turbo -F @hyper/db migrate",
"db:push": "turbo -F @hyper/db push",
diff --git a/packages/api/src/router/recap.ts b/packages/api/src/router/recap.ts
index 8a4d142..46f2001 100644
--- a/packages/api/src/router/recap.ts
+++ b/packages/api/src/router/recap.ts
@@ -24,6 +24,17 @@ import {
} from "../utils/glucose";
export const recapRouter = {
+ all: protectedProcedure.query(async ({ ctx }) => {
+ const userId = ctx.user.id;
+
+ const rows = await ctx.db.query.DailyRecap.findMany({
+ where: eq(DailyRecap.profileId, userId),
+ orderBy: desc(DailyRecap.date),
+ });
+
+ return rows;
+ }),
+
getDailyRecaps: protectedProcedure
.input(DateRangeSchema)
.query(async ({ ctx, input }) => {
diff --git a/packages/db/migrations/0005_stiff_landau.sql b/packages/db/migrations/0005_stiff_landau.sql
new file mode 100644
index 0000000..8e29902
--- /dev/null
+++ b/packages/db/migrations/0005_stiff_landau.sql
@@ -0,0 +1,6 @@
+CREATE INDEX IF NOT EXISTS "cgm_data_profile_id_idx" ON "hyper_cgm_data" USING btree ("profile_id");--> statement-breakpoint
+CREATE UNIQUE INDEX IF NOT EXISTS "cgm_data_record_id_idx" ON "hyper_cgm_data" USING btree ("record_id");--> statement-breakpoint
+CREATE UNIQUE INDEX IF NOT EXISTS "daily_recap_profile_id_date_idx" ON "hyper_daily_recap" USING btree ("profile_id","date");--> statement-breakpoint
+CREATE INDEX IF NOT EXISTS "name_idx" ON "hyper_profile" USING btree ("name");--> statement-breakpoint
+CREATE UNIQUE INDEX IF NOT EXISTS "email_idx" ON "hyper_profile" USING btree ("email");--> statement-breakpoint
+ALTER TABLE "hyper_daily_recap" ADD CONSTRAINT "hyper_daily_recap_date_profile_id_unique" UNIQUE("date","profile_id");
\ No newline at end of file
diff --git a/packages/db/migrations/meta/0005_snapshot.json b/packages/db/migrations/meta/0005_snapshot.json
new file mode 100644
index 0000000..81cd2a3
--- /dev/null
+++ b/packages/db/migrations/meta/0005_snapshot.json
@@ -0,0 +1,712 @@
+{
+ "id": "967d30c3-8ae0-4d29-af92-6bc8e1648548",
+ "prevId": "0156dd1e-57ec-4f08-b9dc-534abd95681f",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.hyper_activity": {
+ "name": "hyper_activity",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "duration": {
+ "name": "duration",
+ "type": "interval",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "activity_type_id": {
+ "name": "activity_type_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "hyper_activity_activity_type_id_hyper_activity_type_id_fk": {
+ "name": "hyper_activity_activity_type_id_hyper_activity_type_id_fk",
+ "tableFrom": "hyper_activity",
+ "tableTo": "hyper_activity_type",
+ "columnsFrom": [
+ "activity_type_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "hyper_activity_profile_id_hyper_profile_id_fk": {
+ "name": "hyper_activity_profile_id_hyper_profile_id_fk",
+ "tableFrom": "hyper_activity",
+ "tableTo": "hyper_profile",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.hyper_activity_type": {
+ "name": "hyper_activity_type",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "hyper_activity_type_name_unique": {
+ "name": "hyper_activity_type_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ }
+ },
+ "auth.users": {
+ "name": "users",
+ "schema": "auth",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.hyper_cgm_data": {
+ "name": "hyper_cgm_data",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dexcom_user_id": {
+ "name": "dexcom_user_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "record_id": {
+ "name": "record_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "system_time": {
+ "name": "system_time",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_time": {
+ "name": "display_time",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transmitter_id": {
+ "name": "transmitter_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "transmitter_ticks": {
+ "name": "transmitter_ticks",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "glucose_value": {
+ "name": "glucose_value",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trend": {
+ "name": "trend",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "trend_rate": {
+ "name": "trend_rate",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "unit": {
+ "name": "unit",
+ "type": "varchar(10)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rate_unit": {
+ "name": "rate_unit",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "display_device": {
+ "name": "display_device",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transmitter_generation": {
+ "name": "transmitter_generation",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "cgm_data_profile_id_idx": {
+ "name": "cgm_data_profile_id_idx",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "cgm_data_record_id_idx": {
+ "name": "cgm_data_record_id_idx",
+ "columns": [
+ {
+ "expression": "record_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "hyper_cgm_data_profile_id_hyper_profile_id_fk": {
+ "name": "hyper_cgm_data_profile_id_hyper_profile_id_fk",
+ "tableFrom": "hyper_cgm_data",
+ "tableTo": "hyper_profile",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "hyper_cgm_data_record_id_unique": {
+ "name": "hyper_cgm_data_record_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "record_id"
+ ]
+ }
+ }
+ },
+ "public.hyper_daily_recap": {
+ "name": "hyper_daily_recap",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "date": {
+ "name": "date",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "average_glucose": {
+ "name": "average_glucose",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "minimum_glucose": {
+ "name": "minimum_glucose",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "maximum_glucose": {
+ "name": "maximum_glucose",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "glucose_variability": {
+ "name": "glucose_variability",
+ "type": "numeric",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "time_in_ranges": {
+ "name": "time_in_ranges",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_readings": {
+ "name": "total_readings",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "daily_recap_profile_id_date_idx": {
+ "name": "daily_recap_profile_id_date_idx",
+ "columns": [
+ {
+ "expression": "profile_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "date",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "hyper_daily_recap_profile_id_hyper_profile_id_fk": {
+ "name": "hyper_daily_recap_profile_id_hyper_profile_id_fk",
+ "tableFrom": "hyper_daily_recap",
+ "tableTo": "hyper_profile",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "hyper_daily_recap_date_profile_id_unique": {
+ "name": "hyper_daily_recap_date_profile_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "date",
+ "profile_id"
+ ]
+ }
+ }
+ },
+ "public.hyper_profile": {
+ "name": "hyper_profile",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image": {
+ "name": "image",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_synced_time": {
+ "name": "last_synced_time",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "diabetes_status": {
+ "name": "diabetes_status",
+ "type": "diabetes_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'none'"
+ },
+ "glucose_range_type": {
+ "name": "glucose_range_type",
+ "type": "glucose_range_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'tight'"
+ }
+ },
+ "indexes": {
+ "name_idx": {
+ "name": "name_idx",
+ "columns": [
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "email_idx": {
+ "name": "email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "hyper_profile_id_users_id_fk": {
+ "name": "hyper_profile_id_users_id_fk",
+ "tableFrom": "hyper_profile",
+ "tableTo": "users",
+ "schemaTo": "auth",
+ "columnsFrom": [
+ "id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.hyper_meal": {
+ "name": "hyper_meal",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "meal_time": {
+ "name": "meal_time",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "carbohydrates": {
+ "name": "carbohydrates",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dietary_energy": {
+ "name": "dietary_energy",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "dietary_sugar": {
+ "name": "dietary_sugar",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "fiber": {
+ "name": "fiber",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "protein": {
+ "name": "protein",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "total_fat": {
+ "name": "total_fat",
+ "type": "double precision",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "hyper_meal_profile_id_hyper_profile_id_fk": {
+ "name": "hyper_meal_profile_id_hyper_profile_id_fk",
+ "tableFrom": "hyper_meal",
+ "tableTo": "hyper_profile",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.hyper_report": {
+ "name": "hyper_report",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_id": {
+ "name": "profile_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "hyper_report_profile_id_hyper_profile_id_fk": {
+ "name": "hyper_report_profile_id_hyper_profile_id_fk",
+ "tableFrom": "hyper_report",
+ "tableTo": "hyper_profile",
+ "columnsFrom": [
+ "profile_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {
+ "public.diabetes_status": {
+ "name": "diabetes_status",
+ "schema": "public",
+ "values": [
+ "none",
+ "pre",
+ "type1",
+ "type2",
+ "type3"
+ ]
+ },
+ "public.glucose_range_type": {
+ "name": "glucose_range_type",
+ "schema": "public",
+ "values": [
+ "standard",
+ "tight",
+ "optimal"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/packages/db/migrations/meta/_journal.json b/packages/db/migrations/meta/_journal.json
index 5cf8622..7836916 100644
--- a/packages/db/migrations/meta/_journal.json
+++ b/packages/db/migrations/meta/_journal.json
@@ -36,6 +36,13 @@
"when": 1725518765059,
"tag": "0004_true_doctor_strange",
"breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "7",
+ "when": 1725660493714,
+ "tag": "0005_stiff_landau",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/packages/db/src/schema/cgm-data.ts b/packages/db/src/schema/cgm-data.ts
index f3f39da..01ea39a 100644
--- a/packages/db/src/schema/cgm-data.ts
+++ b/packages/db/src/schema/cgm-data.ts
@@ -1,8 +1,10 @@
import { relations } from "drizzle-orm";
import {
doublePrecision,
+ index,
integer,
timestamp,
+ uniqueIndex,
uuid,
varchar,
} from "drizzle-orm/pg-core";
@@ -12,35 +14,44 @@ import { z } from "zod";
import { createTable } from "./_table";
import { Profile } from "./profile";
-export const CGMData = createTable("cgm_data", {
- id: uuid("id").primaryKey().defaultRandom(),
- dexcomUserId: varchar("dexcom_user_id", { length: 255 }).notNull(),
- recordId: varchar("record_id", { length: 255 }).notNull().unique(),
- systemTime: timestamp("system_time", { withTimezone: true }).notNull(),
- displayTime: timestamp("display_time", { withTimezone: true }).notNull(),
- transmitterId: varchar("transmitter_id", { length: 255 }),
- transmitterTicks: integer("transmitter_ticks").notNull(),
- glucoseValue: integer("glucose_value"),
- status: varchar("status", { length: 20 }),
- trend: varchar("trend", { length: 20 }),
- trendRate: doublePrecision("trend_rate"),
- unit: varchar("unit", { length: 10 }).notNull(),
- rateUnit: varchar("rate_unit", { length: 20 }).notNull(),
- displayDevice: varchar("display_device", { length: 20 }).notNull(),
- transmitterGeneration: varchar("transmitter_generation", {
- length: 20,
- }).notNull(),
+export const CGMData = createTable(
+ "cgm_data",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ dexcomUserId: varchar("dexcom_user_id", { length: 255 }).notNull(),
+ recordId: varchar("record_id", { length: 255 }).notNull().unique(),
+ systemTime: timestamp("system_time", { withTimezone: true }).notNull(),
+ displayTime: timestamp("display_time", { withTimezone: true }).notNull(),
+ transmitterId: varchar("transmitter_id", { length: 255 }),
+ transmitterTicks: integer("transmitter_ticks").notNull(),
+ glucoseValue: integer("glucose_value"),
+ status: varchar("status", { length: 20 }),
+ trend: varchar("trend", { length: 20 }),
+ trendRate: doublePrecision("trend_rate"),
+ unit: varchar("unit", { length: 10 }).notNull(),
+ rateUnit: varchar("rate_unit", { length: 20 }).notNull(),
+ displayDevice: varchar("display_device", { length: 20 }).notNull(),
+ transmitterGeneration: varchar("transmitter_generation", {
+ length: 20,
+ }).notNull(),
- profileId: uuid("profile_id")
- .notNull()
- .references(() => Profile.id),
+ profileId: uuid("profile_id")
+ .notNull()
+ .references(() => Profile.id),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- updatedAt: timestamp("updatedAt", {
- mode: "date",
- withTimezone: true,
- }).$onUpdateFn(() => new Date()),
-});
+ createdAt: timestamp("created_at").defaultNow().notNull(),
+ updatedAt: timestamp("updatedAt", {
+ mode: "date",
+ withTimezone: true,
+ }).$onUpdateFn(() => new Date()),
+ },
+ (table) => {
+ return {
+ profileIdIdx: index("cgm_data_profile_id_idx").on(table.profileId),
+ recordIdIdx: uniqueIndex("cgm_data_record_id_idx").on(table.recordId),
+ };
+ },
+);
export const CGMDataRelations = relations(CGMData, ({ one }) => ({
profile: one(Profile, {
diff --git a/packages/db/src/schema/daily-recap.ts b/packages/db/src/schema/daily-recap.ts
index 48e7ac8..a9c7d76 100644
--- a/packages/db/src/schema/daily-recap.ts
+++ b/packages/db/src/schema/daily-recap.ts
@@ -6,6 +6,8 @@ import {
jsonb,
numeric,
timestamp,
+ unique,
+ uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
@@ -27,26 +29,41 @@ export interface TimeInRanges {
veryHigh: number; // >250 mg/dL and <5%
}
-export const DailyRecap = createTable("daily_recap", {
- id: uuid("id").primaryKey().defaultRandom(),
- date: date("date", { mode: "date" }).notNull(),
- averageGlucose: integer("average_glucose"),
- minimumGlucose: integer("minimum_glucose"),
- maximumGlucose: integer("maximum_glucose"),
- glucoseVariability: numeric("glucose_variability"), // Standard deviation or coefficient of variation
- timeInRanges: jsonb("time_in_ranges").$type(),
- totalReadings: integer("total_readings"),
+export const DailyRecap = createTable(
+ "daily_recap",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ date: date("date", { mode: "date" }).notNull(),
+ averageGlucose: integer("average_glucose"),
+ minimumGlucose: integer("minimum_glucose"),
+ maximumGlucose: integer("maximum_glucose"),
+ glucoseVariability: numeric("glucose_variability"), // Standard deviation or coefficient of variation
+ timeInRanges: jsonb("time_in_ranges").$type(),
+ totalReadings: integer("total_readings"),
- profileId: uuid("profile_id")
- .notNull()
- .references(() => Profile.id),
+ profileId: uuid("profile_id")
+ .notNull()
+ .references(() => Profile.id),
- createdAt: timestamp("created_at").defaultNow().notNull(),
- updatedAt: timestamp("updatedAt", {
- mode: "date",
- withTimezone: true,
- }).$onUpdateFn(() => new Date()),
-});
+ createdAt: timestamp("created_at").defaultNow().notNull(),
+ updatedAt: timestamp("updatedAt", {
+ mode: "date",
+ withTimezone: true,
+ }).$onUpdateFn(() => new Date()),
+ },
+ (table) => {
+ return {
+ profileIdDateIdx: uniqueIndex("daily_recap_profile_id_date_idx").on(
+ table.profileId,
+ table.date,
+ ),
+ // This unique constraint allows for efficient upserts
+ // It ensures only one recap per day per profile
+ // and enables the use of ON CONFLICT for updates
+ dateProfileUnique: unique().on(table.date, table.profileId),
+ };
+ },
+);
export const DailyRecapRelations = relations(DailyRecap, ({ one }) => ({
profile: one(Profile, {
diff --git a/packages/db/src/schema/profile.ts b/packages/db/src/schema/profile.ts
index c9da1dd..cdb97ae 100644
--- a/packages/db/src/schema/profile.ts
+++ b/packages/db/src/schema/profile.ts
@@ -1,5 +1,12 @@
import { relations } from "drizzle-orm";
-import { pgEnum, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
+import {
+ index,
+ pgEnum,
+ timestamp,
+ uniqueIndex,
+ uuid,
+ varchar,
+} from "drizzle-orm/pg-core";
import { createTable } from "./_table";
import { Users } from "./auth";
@@ -22,22 +29,31 @@ const diabetesStatus = ["none", "pre", "type1", "type2", "type3"] as const;
export type DiabetesStatus = (typeof diabetesStatus)[number];
export const diabetesStatusEnum = pgEnum("diabetes_status", diabetesStatus);
-export const Profile = createTable("profile", {
- // Matches id from auth.users table in Supabase
- id: uuid("id")
- .primaryKey()
- .references(() => Users.id, { onDelete: "cascade" }),
- name: varchar("name", { length: 256 }).notNull(),
- image: varchar("image", { length: 256 }),
- email: varchar("email", { length: 256 }),
- lastSyncedTime: timestamp("last_synced_time", { withTimezone: true }),
- diabetesStatus: diabetesStatusEnum("diabetes_status")
- .notNull()
- .default("none"),
- glucoseRangeType: glucoseRangeTypeEnum("glucose_range_type")
- .notNull()
- .default("tight"),
-});
+export const Profile = createTable(
+ "profile",
+ {
+ // Matches id from auth.users table in Supabase
+ id: uuid("id")
+ .primaryKey()
+ .references(() => Users.id, { onDelete: "cascade" }),
+ name: varchar("name", { length: 256 }).notNull(),
+ image: varchar("image", { length: 256 }),
+ email: varchar("email", { length: 256 }),
+ lastSyncedTime: timestamp("last_synced_time", { withTimezone: true }),
+ diabetesStatus: diabetesStatusEnum("diabetes_status")
+ .notNull()
+ .default("none"),
+ glucoseRangeType: glucoseRangeTypeEnum("glucose_range_type")
+ .notNull()
+ .default("tight"),
+ },
+ (table) => {
+ return {
+ nameIdx: index("name_idx").on(table.name),
+ emailIdx: uniqueIndex("email_idx").on(table.email),
+ };
+ },
+);
export const ProfileRelations = relations(Profile, ({ many }) => ({
cgmData: many(CGMData),
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 873ef6b..b9e21fb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -51,11 +51,11 @@ catalogs:
specifier: ^0.5.0
version: 0.5.0
'@supabase/ssr':
- specifier: ^0.5.0
- version: 0.5.0
+ specifier: ^0.5.1
+ version: 0.5.1
'@supabase/supabase-js':
- specifier: ^2.45.2
- version: 2.45.2
+ specifier: ^2.45.3
+ version: 2.45.3
tailwind:
tailwindcss:
specifier: ^3.4.10
@@ -152,6 +152,12 @@ importers:
'@rn-primitives/progress':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/radio-group':
+ specifier: ^1.0.3
+ version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/select':
+ specifier: ^1.0.4
+ version: 1.0.4(@rn-primitives/portal@1.0.3(@types/react@18.3.4)(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
'@rn-primitives/separator':
specifier: ^1.0.3
version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
@@ -175,10 +181,10 @@ importers:
version: 1.3.11(react-native-reanimated@3.15.1(@babel/core@7.25.2)(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
'@supabase/auth-helpers-react':
specifier: catalog:supabase
- version: 0.5.0(@supabase/supabase-js@2.45.2(bufferutil@4.0.8))
+ version: 0.5.0(@supabase/supabase-js@2.45.3(bufferutil@4.0.8))
'@supabase/supabase-js':
specifier: catalog:supabase
- version: 2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+ version: 2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)
'@tanstack/react-query':
specifier: 'catalog:'
version: 5.52.1(react@18.3.1)
@@ -484,10 +490,10 @@ importers:
version: link:../../packages/validators
'@supabase/ssr':
specifier: catalog:supabase
- version: 0.5.0(@supabase/supabase-js@2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4))
+ version: 0.5.1(@supabase/supabase-js@2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4))
'@supabase/supabase-js':
specifier: catalog:supabase
- version: 2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+ version: 2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)
'@t3-oss/env-nextjs':
specifier: ^0.11.1
version: 0.11.1(typescript@5.5.4)(zod@3.23.8)
@@ -602,7 +608,7 @@ importers:
version: link:../validators
'@supabase/supabase-js':
specifier: catalog:supabase
- version: 2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+ version: 2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)
'@trpc/server':
specifier: 'catalog:'
version: 11.0.0-rc.485
@@ -810,7 +816,7 @@ importers:
version: 7.35.0(eslint@9.9.1(jiti@1.21.6))
eslint-plugin-react-hooks:
specifier: rc
- version: 5.1.0-rc-d1afcb43-20240903(eslint@9.9.1(jiti@1.21.6))
+ version: 5.1.0-rc-a03254bc-20240905(eslint@9.9.1(jiti@1.21.6))
eslint-plugin-testing-library:
specifier: ^6.3.0
version: 6.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)
@@ -3042,6 +3048,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-radio-group@1.2.0':
+ resolution: {integrity: sha512-yv+oiLaicYMBpqgfpSPw6q+RyXlLdIpQWDHZbUKURxe+nEh53hFXPPlfhfQQtYkS5MMK/5IWIa76SksleQZSzw==}
+ 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-roving-focus@1.1.0':
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
peerDependencies:
@@ -3068,6 +3087,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-select@2.1.1':
+ resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==}
+ 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.0':
resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==}
peerDependencies:
@@ -3214,6 +3246,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-use-previous@1.1.0':
+ resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-use-rect@1.1.0':
resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
peerDependencies:
@@ -3552,6 +3593,31 @@ packages:
react-native-web:
optional: true
+ '@rn-primitives/radio-group@1.0.3':
+ resolution: {integrity: sha512-VIuWiz2WAM1SbCQJThZ/3S2naAXO+ijgROBCbCKcp3H32G0rHn/9M8GReijb8vdqVPuM+CFdoF1DBI2uPyQVDw==}
+ peerDependencies:
+ react: '*'
+ react-native: '*'
+ react-native-web: '*'
+ peerDependenciesMeta:
+ react-native:
+ optional: true
+ react-native-web:
+ optional: true
+
+ '@rn-primitives/select@1.0.4':
+ resolution: {integrity: sha512-n1qTOjwYgATIEdKC7/Ox5Kjr4zi8zYxAaW0tyh0N16qvWCH2gjAEytvbIimZC/Gn+Q6NiqGparv9jxH5myZW4g==}
+ peerDependencies:
+ '@rn-primitives/portal': '*'
+ react: '*'
+ react-native: '*'
+ react-native-web: '*'
+ peerDependenciesMeta:
+ react-native:
+ optional: true
+ react-native-web:
+ optional: true
+
'@rn-primitives/separator@1.0.3':
resolution: {integrity: sha512-8X6NS78hCUeJSs3CeVMnK6ndVX+W+Gb07qP/xY0KfKpILXIEbpnyicWR1oMpt32dTbMmBJAptLJKdcj+s4Fq+A==}
peerDependencies:
@@ -3617,11 +3683,6 @@ packages:
resolution: {integrity: sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==}
engines: {node: '>=14.15'}
- '@rollup/rollup-linux-x64-gnu@4.21.0':
- resolution: {integrity: sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==}
- cpu: [x64]
- os: [linux]
-
'@scena/dragscroll@1.4.0':
resolution: {integrity: sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==}
@@ -3677,8 +3738,8 @@ packages:
peerDependencies:
'@supabase/supabase-js': ^2.39.8
- '@supabase/auth-js@2.64.4':
- resolution: {integrity: sha512-9ITagy4WP4FLl+mke1rchapOH0RQpf++DI+WSG2sO1OFOZ0rW3cwAM0nCrMOxu+Zw4vJ4zObc08uvQrXx590Tg==}
+ '@supabase/auth-js@2.65.0':
+ resolution: {integrity: sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==}
'@supabase/functions-js@2.4.1':
resolution: {integrity: sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==}
@@ -3693,16 +3754,16 @@ packages:
'@supabase/realtime-js@2.10.2':
resolution: {integrity: sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==}
- '@supabase/ssr@0.5.0':
- resolution: {integrity: sha512-5E0NmPpfXBzfATgYhg7o/nPkVu+38mx7pO5vlhxnk/5sYGnIZcpMHJs3jONb7Jx5IJN2kTPb59luEw66MOOTaA==}
+ '@supabase/ssr@0.5.1':
+ resolution: {integrity: sha512-+G94H/GZG0nErZ3FQV9yJmsC5Rj7dmcfCAwOt37hxeR1La+QTl8cE9whzYwPUrTJjMLGNXoO+1BMvVxwBAbz4g==}
peerDependencies:
'@supabase/supabase-js': ^2.43.4
'@supabase/storage-js@2.7.0':
resolution: {integrity: sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==}
- '@supabase/supabase-js@2.45.2':
- resolution: {integrity: sha512-kJKY3ISFusVKQWCP8Kqo20Ebxy2WLp6Ry/Suco0aQsPXH7bvn7clswsdhcfcH/5Tr0MYz/jcCjF0n/27SetiCw==}
+ '@supabase/supabase-js@2.45.3':
+ resolution: {integrity: sha512-4wAux6cuVMrdH/qUjKn6p3p3L9AtAO3Une6ojIrtpCj1RaXKVoyIATiacSRAI+pKff6XZBVCGC29v+z4Jo/uSw==}
'@swc/counter@0.1.3':
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
@@ -5641,8 +5702,8 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
- eslint-plugin-react-hooks@5.1.0-rc-d1afcb43-20240903:
- resolution: {integrity: sha512-/nFI+l7fwC5yg0VIPL9YmtlAPSbzOeeMW7cIcx5JEcsdE8SBN7uXKnsfik1yDQeGbQIgaQzN4nj5fNxfoVh7dQ==}
+ eslint-plugin-react-hooks@5.1.0-rc-a03254bc-20240905:
+ resolution: {integrity: sha512-MAQG2WZjBdVBz2mpyWz2l7piMjBoHrSaPiWAQqyJuwbmRRy5YHdVW/jkLXQcKWoct+U02AojaWh+EGKfsL1fgA==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
@@ -12633,6 +12694,24 @@ snapshots:
'@types/react': 18.3.4
'@types/react-dom': 18.3.0
+ '@radix-ui/react-radio-group@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-presence': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.4
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.0
@@ -12667,6 +12746,35 @@ snapshots:
'@types/react': 18.3.4
'@types/react-dom': 18.3.0
+ '@radix-ui/react-select@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1)
+ '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ aria-hidden: 1.2.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-remove-scroll: 2.5.7(@types/react@18.3.4)(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.4
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-separator@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -12801,6 +12909,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.4
+ '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.4)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.4
+
'@radix-ui/react-use-rect@1.1.0(@types/react@18.3.4)(react@18.3.1)':
dependencies:
'@radix-ui/rect': 1.1.0
@@ -13353,6 +13467,36 @@ snapshots:
- '@types/react-dom'
- react-dom
+ '@rn-primitives/radio-group@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-radio-group': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/slot': 1.0.3(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/types': 1.0.3(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ react-native: 0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1)
+ react-native-web: 0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ - react-dom
+
+ '@rn-primitives/select@1.0.4(@rn-primitives/portal@1.0.3(@types/react@18.3.4)(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1))(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-select': 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/hooks': 1.0.3(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/portal': 1.0.3(@types/react@18.3.4)(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/slot': 1.0.3(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ '@rn-primitives/types': 1.0.3(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ react-native: 0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1)
+ react-native-web: 0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ - react-dom
+
'@rn-primitives/separator@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react-native-web@0.19.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.4)(bufferutil@4.0.8)(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-separator': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -13415,9 +13559,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@rollup/rollup-linux-x64-gnu@4.21.0':
- optional: true
-
'@scena/dragscroll@1.4.0':
dependencies:
'@daybrush/utils': 1.13.0
@@ -13471,11 +13612,11 @@ snapshots:
dependencies:
'@sinonjs/commons': 3.0.1
- '@supabase/auth-helpers-react@0.5.0(@supabase/supabase-js@2.45.2(bufferutil@4.0.8))':
+ '@supabase/auth-helpers-react@0.5.0(@supabase/supabase-js@2.45.3(bufferutil@4.0.8))':
dependencies:
- '@supabase/supabase-js': 2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+ '@supabase/supabase-js': 2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)
- '@supabase/auth-js@2.64.4':
+ '@supabase/auth-js@2.65.0':
dependencies:
'@supabase/node-fetch': 2.6.15
@@ -13501,20 +13642,18 @@ snapshots:
- bufferutil
- utf-8-validate
- '@supabase/ssr@0.5.0(@supabase/supabase-js@2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
+ '@supabase/ssr@0.5.1(@supabase/supabase-js@2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4))':
dependencies:
- '@supabase/supabase-js': 2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)
+ '@supabase/supabase-js': 2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)
cookie: 0.6.0
- optionalDependencies:
- '@rollup/rollup-linux-x64-gnu': 4.21.0
'@supabase/storage-js@2.7.0':
dependencies:
'@supabase/node-fetch': 2.6.15
- '@supabase/supabase-js@2.45.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
+ '@supabase/supabase-js@2.45.3(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
dependencies:
- '@supabase/auth-js': 2.64.4
+ '@supabase/auth-js': 2.65.0
'@supabase/functions-js': 2.4.1
'@supabase/node-fetch': 2.6.15
'@supabase/postgrest-js': 1.15.8
@@ -15746,7 +15885,7 @@ snapshots:
safe-regex-test: 1.0.3
string.prototype.includes: 2.0.0
- eslint-plugin-react-hooks@5.1.0-rc-d1afcb43-20240903(eslint@9.9.1(jiti@1.21.6)):
+ eslint-plugin-react-hooks@5.1.0-rc-a03254bc-20240905(eslint@9.9.1(jiti@1.21.6)):
dependencies:
eslint: 9.9.1(jiti@1.21.6)
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index b022333..4907a35 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -21,8 +21,8 @@ catalogs:
"@types/react": ^18.3.3
"@types/react-dom": ^18.3.0
supabase:
- "@supabase/supabase-js": ^2.45.2
- "@supabase/ssr": ^0.5.0
+ "@supabase/supabase-js": ^2.45.3
+ "@supabase/ssr": ^0.5.1
"@supabase/auth-helpers-react": ^0.5.0
tailwind:
tailwindcss: ^3.4.10