Skip to content

Commit

Permalink
chore: update README for stable
Browse files Browse the repository at this point in the history
  • Loading branch information
trevorpfiz committed Nov 12, 2024
1 parent 58bf0c4 commit b7f4166
Show file tree
Hide file tree
Showing 105 changed files with 13,382 additions and 7,618 deletions.
2 changes: 1 addition & 1 deletion .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"extends": ["config:base"],
"packageRules": [
{
"matchPackagePatterns": ["^@hyper/"],
"matchPackagePatterns": ["^@stable/"],
"enabled": false
}
],
Expand Down
File renamed without changes.
156 changes: 102 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
<!-- PROJECT LOGO -->
<br>
<div align="center">
<a href="https://github.com/trevorpfiz/wellchart">
<img src="https://github.com/trevorpfiz/wellchart/assets/24904780/0e15369e-2550-42da-84ad-0f224af1ac8a" alt="Logo" width="80" height="80">
<a href="https://github.com/trevorpfiz/stable">
<img src="https://github.com/trevorpfiz/stable/assets/24904780/0e15369e-2550-42da-84ad-0f224af1ac8a" alt="Logo" width="80" height="80">
</a>

<h3 align="center">WellChart - Automated Pre-Charting for Digital Health</h3>
<h3 align="center">Stable - Open Source Metabolic Health App</h3>

<p align="center">
This is an open source AI ambient scribe app for healthcare. Record patient-doctor conversations and automatically generate SOAP notes based on the transcripts. Make sure you are HIPAA compliant before using this in a healthcare setting.
This is an open source metabolic health app. Connect with your Dexcom CGM and visualize your glucose data in real-time.
</p>

<!-- WIP Alert -->
<p align="center">
<strong>⚠️ WIP - Upgrading to Expo SDK 52 and the New Architecture. ⚠️</strong>
</p>
</p>
</div>

Expand Down Expand Up @@ -46,25 +52,21 @@

<!-- PROJECT DETAILS -->


## Project Details

### Apps

- **Expo** mobile app for recording patient-doctor conversations.
- **Next.js** dashboard for managing conversations and SOAP notes.
- **FastAPI** for processing audio recordings, generating transcripts, and generating SOAP notes.
- **Expo** mobile app for optimizing metabolic health.
- **Next.js** web dashboard.

### Features

- **Animations:** `react-native-reanimated` for OpenAI-like animations.
- **Authentication:** Sign up with Google, Apple, or email. Clerk components for Next.js, custom components (signin, signup, OTP, password reset) for Expo.
- **Audio Recording:** Record patient-doctor conversations using `expo-av`.
- **Transcripts + SOAP Notes:** Generated in FastAPI using OpenAI APIs.
- **Web Dashboard:** Next.js app for managing SOAP notes.
- **Note Editor:** Edit generated SOAP notes using [novel](https://novel.sh/).

Refer to [building-hipaa-compliant](https://github.com/zacharypfiz/building-hipaa-compliant) for more information on making this project HIPAA compliant.
- **Authentication:** Sign up with Apple, Google, or email. Supabase Auth.
- **Calendar:** WHOOP-inspired calendar built with Flash Calendar.
- **Day Slider:** Quickly swipe through days of data.
- **Dexcom Sandbox:** Connect with Dexcom Sandbox Data.
- **Glucose Graph:** Visualize your Time in Range (TIR) with charts from Victory Native.
- **Web Dashboard:** Next.js app for visualizing metabolic health.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

Expand All @@ -76,17 +78,15 @@ Refer to [building-hipaa-compliant](https://github.com/zacharypfiz/building-hipa

- [React Native](https://reactnative.dev/)
- [Expo](https://expo.dev/)
- [NativeWind](https://www.nativewind.dev/v4/overview)
- [NativeWind](https://www.nativewind.dev/)
- [TypeScript](https://www.typescriptlang.org/)
- [tRPC](https://trpc.io/)
- [Drizzle ORM](https://orm.drizzle.team/)
- [Clerk](https://clerk.com/)
- [Next.js](https://nextjs.org/)
- [Tailwind CSS](https://tailwindcss.com/)
- [FastAPI](https://fastapi.tiangolo.com/)
- [SST Ion](https://ion.sst.dev/)
- [Supabase](https://supabase.com/)
- [Supabase Auth](https://supabase.com/docs/guides/auth)
- [Jest](https://jestjs.io/)
- [Testing Library](https://testing-library.com/)
- [Turborepo](https://turbo.build/repo/docs)

```text
Expand All @@ -98,33 +98,26 @@ Refer to [building-hipaa-compliant](https://github.com/zacharypfiz/building-hipa
└─ Multi-root Workspaces for smoother python experience in monorepo
apps
├─ expo
| ├─ Expo SDK 51
| ├─ React Native using React 18
| ├─ Expo SDK 52
| ├─ React Native 0.76 New Architecture
| ├─ Navigation using Expo Router
| ├─ Tailwind using NativeWind
| ├─ Typesafe API calls using tRPC
| └─ Jest + React Native Testing Library for unit tests
├─ nextjs
| ├─ Next.js 14
| ├─ React 18
| ├─ Tailwind CSS
| └─ E2E Typesafe API Server & Client
└─ fastapi
├─ FastAPI for uploading and processing audio recordings
├─ OpenAI Whisper for transcription
└─ OpenAI Chat Completions API for generating SOAP notes from transcripts
└─ nextjs
├─ Next.js 14
├─ React 18
├─ Tailwind CSS
└─ E2E Typesafe API Server & Client
packages
├─ api
| ├─ tRPC v11 router definition.
| └─ Generated TypeScript client from FastAPI OpenAPI spec.
| └─ tRPC v11 router definition.
├─ db
| └─ Typesafe db calls using Drizzle & Amazon RDS
| └─ Typesafe db calls using Drizzle & Supabase
├─ ui
| └─ shadcn/ui.
└─ validators
└─ Zod schemas for repo-wide type-safety and validation.
infra
└─ SST Ion resources
tooling
├─ eslint
| └─ shared, fine-grained, eslint presets
Expand All @@ -138,7 +131,7 @@ tooling
└─ shared tsconfig you can extend from
```

> In this project, we use `@hyper` as a placeholder for package names. As a user, you might want to replace it with your own organization or project name. You can use find-and-replace to change all the instances of `@hyper` to something like `@my-company` or `@project-name`.
> In this project, we use `@stable` as a placeholder for package names. As a user, you might want to replace it with your own organization or project name. You can use find-and-replace to change all the instances of `@stable` to something like `@my-company` or `@project-name`.
<p align="right">(<a href="#readme-top">back to top</a>)</p>

Expand All @@ -158,22 +151,81 @@ cp .env.example .env
cp .env.example .env.local
```

Configure AWS credentials
<https://docs.sst.dev/advanced/iam-credentials#loading-from-a-file>

Install the SST CLI
<https://ion.sst.dev/docs/reference/cli>

```bash
# Run SST Ion (run this in apps/nextjs)
pnpm dev

# Push the Drizzle schema to the RDS database
# Push the Drizzle schema to Supabase
pnpm db:generate
pnpm db:migrate
```

### 2. Configure Expo `dev`-script
### 2. Setup Supabase

1. Go to [the Supabase dashboard](https://app.supabase.com/projects) and create a new project.
2. Under project settings, retrieve the environment variables `reference id`, `project url` & `anon public key` and paste them into [.env](./.env.example) in the necessary places. You'll also need the database password you set when creating the project.
3. Under `Auth`, configure any auth provider(s) of your choice. This repo is using Github for Web and Apple for Mobile.
4. If you want to use the `Email` provider and `email confirmation`, go to `Auth` -> `Email Templates` and change the `Confirm signup` from `{{ .ConfirmationURL }}` to `{{ .RedirectTo }}&token_hash={{ .TokenHash }}&type=signup`, according to <https://supabase.com/docs/guides/auth/redirect-urls#email-templates-when-using-redirectto>. `.RedirectTo` will need to be added to your `Redirect URLs` in the next step.
5. Under `Auth` -> `URL Configuration`, set the `Site URL` to your production URL and add `http://localhost:3000/**` and `https://*-username.vercel.app/**` to `Redirect URLs` as detailed here <https://supabase.com/docs/guides/auth/redirect-urls#vercel-preview-urls>.
6. Set up a trigger when a new user signs up: <https://supabase.com/docs/guides/auth/managing-user-data#using-triggers>. You can run this in the SQL Editor.

```sql
-- inserts a row into public.profile
create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
insert into public.epi_profile (id, email, name, image)
values (
new.id,
new.email,
COALESCE(
new.raw_user_meta_data ->> 'name',
new.raw_user_meta_data ->> 'full_name',
new.raw_user_meta_data ->> 'user_name',
'Guest User'
),
new.raw_user_meta_data ->> 'avatar_url'
)
on conflict (id) do update set
email = excluded.email,
name = excluded.name,
image = excluded.image;
return new;
end;
$$;

-- trigger the function every time a user is created
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure public.handle_new_user();

-- trigger the function when a user signs in/their email is confirmed to get missing values
create trigger on_auth_user_verified
after update on auth.users
for each row when (
old.last_sign_in_at is null
and new.last_sign_in_at is not null
) execute procedure public.handle_new_user();
```

```sql
-- drop a trigger if needed
drop trigger "on_auth_user_verified" on auth.users;
```

7. Remove access to the `public` schema as we are only using the server

By default, Supabase exposes the `public` schema to the PostgREST API to allow the `supabase-js` client query the database directly from the client. However, since we route all our requests through the Next.js application (through tRPC), we don't want our client to have this access. To disable this, execute the following SQL query in the SQL Editor on your Supabase dashboard:

```sql
REVOKE USAGE ON SCHEMA public FROM anon, authenticated;
```

![disable public access](https://user-images.githubusercontent.com/51714798/231810706-88b1db82-0cfd-485f-9043-ef12a53dc62f.png)

> Note: This means you also don't need to enable row-level security (RLS) on your database if you don't want to.
### 3. Configure Expo `dev`-script

#### Use iOS Simulator

Expand Down Expand Up @@ -241,10 +293,6 @@ This repo originates from [create-t3-turbo](https://github.com/t3-oss/create-t3-

Thanks as well to the following:

- [next-fast-turbo](https://github.com/cording12/next-fast-turbo) for the learnings on how to bring FastAPI into the project.

- [Build a ChatGPT Clone with React Native](https://youtu.be/8ztx68SUOQo?si=f-HCi6K1qpVX-ATV) by Simon Grimm for the inspiring the direction of the mobile app.

- [Andy AI](https://www.ycombinator.com/companies/andy-ai) for fueling the original idea of this project.
- [Levels Health](https://www.levels.com/) for fueling the inspiration for this project.

<p align="right">(<a href="#readme-top">back to top</a>)</p>
6 changes: 3 additions & 3 deletions apps/expo/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import baseConfig from "@hyper/eslint-config/base";
import expoConfig from "@hyper/eslint-config/expo";
import reactConfig from "@hyper/eslint-config/react";
import baseConfig from "@stable/eslint-config/base";
import expoConfig from "@stable/eslint-config/expo";
import reactConfig from "@stable/eslint-config/react";

/** @type {import('typescript-eslint').Config} */
export default [
Expand Down
18 changes: 9 additions & 9 deletions apps/expo/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@hyper/expo",
"name": "@stable/expo",
"version": "0.1.0",
"private": true,
"main": "src/index.ts",
Expand All @@ -25,8 +25,8 @@
"@expo/vector-icons": "^14.0.4",
"@gorhom/bottom-sheet": "^5.0.5",
"@hookform/resolvers": "^3.9.1",
"@hyper/db": "workspace:*",
"@hyper/validators": "workspace:*",
"@stable/db": "workspace:*",
"@stable/validators": "workspace:*",
"@marceloterreiro/flash-calendar": "^1.2.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-progress": "^1.1.0",
Expand Down Expand Up @@ -123,11 +123,11 @@
"@babel/preset-env": "^7.25.9",
"@babel/runtime": "^7.25.9",
"@expo/config-plugins": "^9.0.5",
"@hyper/api": "workspace:*",
"@hyper/eslint-config": "workspace:*",
"@hyper/prettier-config": "workspace:*",
"@hyper/tailwind-config": "workspace:*",
"@hyper/tsconfig": "workspace:*",
"@stable/api": "workspace:*",
"@stable/eslint-config": "workspace:*",
"@stable/prettier-config": "workspace:*",
"@stable/tailwind-config": "workspace:*",
"@stable/tsconfig": "workspace:*",
"@testing-library/jest-dom": "^6.6.2",
"@testing-library/react-native": "^12.7.2",
"@types/aes-js": "^3.1.4",
Expand All @@ -146,5 +146,5 @@
"ts-jest": "^29.2.5",
"typescript": "catalog:"
},
"prettier": "@hyper/prettier-config"
"prettier": "@stable/prettier-config"
}
4 changes: 2 additions & 2 deletions apps/expo/src/components/auth/reset-password-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import Animated, { FadeInDown, FadeOutUp } from "react-native-reanimated";
import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, FormProvider, useForm } from "react-hook-form";

import type { RequestPasswordReset } from "@hyper/validators/auth";
import { RequestPasswordResetSchema } from "@hyper/validators/auth";
import type { RequestPasswordReset } from "@stable/validators/auth";
import { RequestPasswordResetSchema } from "@stable/validators/auth";

import { ResetPasswordVerificationForm } from "~/components/auth/reset-password-verification-form";
import { Button } from "~/components/ui/button";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import Animated, { FadeInDown, FadeOutUp } from "react-native-reanimated";
import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, FormProvider, useForm } from "react-hook-form";

import type { ResetPassword } from "@hyper/validators/auth";
import { ResetPasswordSchema } from "@hyper/validators/auth";
import type { ResetPassword } from "@stable/validators/auth";
import { ResetPasswordSchema } from "@stable/validators/auth";

import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
Expand Down
4 changes: 2 additions & 2 deletions apps/expo/src/components/auth/signin-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { Link } from "expo-router";
import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, FormProvider, useForm } from "react-hook-form";

import type { SignIn } from "@hyper/validators/auth";
import { SignInSchema } from "@hyper/validators/auth";
import type { SignIn } from "@stable/validators/auth";
import { SignInSchema } from "@stable/validators/auth";

import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
Expand Down
4 changes: 2 additions & 2 deletions apps/expo/src/components/auth/signup-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import Animated, { FadeInDown, FadeOutUp } from "react-native-reanimated";
import { zodResolver } from "@hookform/resolvers/zod";
import { Controller, FormProvider, useForm } from "react-hook-form";

import type { SignUp } from "@hyper/validators/auth";
import { SignUpSchema } from "@hyper/validators/auth";
import type { SignUp } from "@stable/validators/auth";
import { SignUpSchema } from "@stable/validators/auth";

import { OTPVerification } from "~/components/auth/otp-verification";
import { Button } from "~/components/ui/button";
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/src/components/calendar/basic-calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { View } from "react-native";
import { Calendar, useCalendar } from "@marceloterreiro/flash-calendar";
import { format } from "date-fns";

import type { DailyRecap, GlucoseRangeTypes } from "@hyper/db/schema";
import type { DailyRecap, GlucoseRangeTypes } from "@stable/db/schema";

import { Button } from "~/components/ui/button";
import { Text } from "~/components/ui/text";
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/src/components/glucose/change-range-setting.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { useSafeAreaInsets } from "react-native-safe-area-context";

import type { GlucoseRangeTypes } from "@hyper/db/schema";
import type { GlucoseRangeTypes } from "@stable/db/schema";

import { Label } from "~/components/ui/label";
import {
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/src/components/home/blood-sugar-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { z } from "zod";
import { View } from "react-native";
import { DateTime } from "luxon";

import type { TrendEnum } from "@hyper/validators/dexcom";
import type { TrendEnum } from "@stable/validators/dexcom";

import { TrendIcon } from "~/components/home/trend-icon";
import { Skeleton } from "~/components/ui/skeleton";
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/src/components/home/day-slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Dimensions, Pressable, View } from "react-native";
import { FlashList } from "@shopify/flash-list";
import { DateTime } from "luxon";

import type { DailyRecap, GlucoseRangeTypes } from "@hyper/db/schema";
import type { DailyRecap, GlucoseRangeTypes } from "@stable/db/schema";

import { Skeleton } from "~/components/ui/skeleton";
import { Text } from "~/components/ui/text";
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/src/components/home/trend-icon.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { LucideProps } from "lucide-react-native";
import type { z } from "zod";

import type { TrendEnum } from "@hyper/validators/dexcom";
import type { TrendEnum } from "@stable/validators/dexcom";

import { ChevronsDown } from "~/lib/icons/chevrons-down";
import { ChevronsUp } from "~/lib/icons/chevrons-up";
Expand Down
2 changes: 1 addition & 1 deletion apps/expo/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ClassValue } from "clsx";
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";

import type { GlucoseRangeTypes } from "@hyper/db/schema";
import type { GlucoseRangeTypes } from "@stable/db/schema";

import { CALENDAR_THEME } from "~/lib/constants";

Expand Down
Loading

0 comments on commit b7f4166

Please sign in to comment.