Skip to content

Commit

Permalink
Merge pull request #174 from mikepsinn/develop
Browse files Browse the repository at this point in the history
Added UserVariables and measurements to Next.js app and updated openapi docs
  • Loading branch information
mikepsinn authored Apr 8, 2024
2 parents db34df2 + 1cdf3f0 commit 92b32b9
Show file tree
Hide file tree
Showing 149 changed files with 16,732 additions and 2,807 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ desktop.ini
coverage/
/apps/js-examples/data
/.42c
/apps/next/
776 changes: 0 additions & 776 deletions apps/api-gateway/prisma/schema.prisma

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/js-examples/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ datasource db {
model users {
id Int @id @default(autoincrement())
email String?
fdai_user_id Int?
first_name String?
username String?
fdai_user_id Int?
fdai_access_token String?
fdai_refresh_token String?
fdai_expires_in Int?
Expand Down
20 changes: 12 additions & 8 deletions apps/nextjs/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@ NODE_ENV=development

# Authentication (NextAuth.js)
NEXTAUTH_URL=http://localhost:3000

# https://generate-secret.vercel.app/32
NEXTAUTH_SECRET=

DATABASE_URL=postgresql://user:pass@localhost:5432/api_gateway

# https://builder.fdai.earth/app/public/#/app/configuration
FDAI_CLIENT_ID=
FDAI_CLIENT_SECRET=

# https://console.cloud.google.com/apis/credentials
# Callback URL: http://localhost:3000/api/auth/callback/google
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# https://github.com/settings/developers
# https://github.com/settings/applications
# Callback URL: http://localhost:3000/api/auth/callback/github
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

# Database
DATABASE_URL="postgresql://user:pass@localhost:5432/fdai_test?schema=public"

# Get this from https://platform.openai.com/account/api-keys
OPENAI_API_KEY=
OPENAI_MODEL=

# Get your client ID/secret from https://builder.fdai.earth/app/public/#/app/configuration
FDAI_CLIENT_ID=
FDAI_CLIENT_SECRET=
3 changes: 0 additions & 3 deletions apps/nextjs/.eslintrc.json

This file was deleted.

2 changes: 2 additions & 0 deletions apps/nextjs/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
13 changes: 11 additions & 2 deletions apps/nextjs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage
Expand All @@ -26,6 +25,7 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
.env*.local

# vercel
Expand All @@ -34,4 +34,13 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
/.env

# extra
/private_docs
.vercel

# IDE
.idea/

# Docker Compose Volumes
/data/
5 changes: 5 additions & 0 deletions apps/nextjs/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dist
node_modules
.next
package.json
pnpm-lock.yaml
5 changes: 4 additions & 1 deletion apps/nextjs/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"WillLuke.nextjs.addTypesOnSave": true,
"WillLuke.nextjs.hasPrompted": true
"WillLuke.nextjs.hasPrompted": true,
"files.associations": {
"*.mdx": "markdown"
}
}
27 changes: 27 additions & 0 deletions apps/nextjs/app/(frontpage)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getCurrentUser } from "@/lib/session"
import Footer from "@/components/layout/footer"
import Navbar from "@/components/layout/navbar"

interface FrontPageLayoutProps {
children: React.ReactNode
}

export default async function FrontPageLayout({
children,
}: FrontPageLayoutProps) {
const user = await getCurrentUser()

return (
<>
<Navbar
user={{
name: user?.name,
image: user?.image,
email: user?.email,
}}
/>
{children}
<Footer />
</>
)
}
9 changes: 9 additions & 0 deletions apps/nextjs/app/(frontpage)/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Icons } from "@/components/icons"

export default function HomeLoading() {
return (
<main className="flex justify-center p-8">
<Icons.spinner className="animate-spin text-4xl" />
</main>
)
}
11 changes: 11 additions & 0 deletions apps/nextjs/app/(frontpage)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Hero from "@/components/pages/hero"
import { PWARedirect } from "@/components/pwa-redirect"

export default function Home() {
return (
<main>
<Hero />
<PWARedirect />
</main>
)
}
17 changes: 17 additions & 0 deletions apps/nextjs/app/api.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import type { paths } from './fdai';
import createClient from 'openapi-fetch';
import { getCurrentUser } from '@/lib/session';

let bearerToken = "demo"

export let { GET, POST, PATCH, PUT, DELETE, HEAD, TRACE } = createClient<paths>(
{
baseUrl: 'https://safe.fdai.earth/api/v3',
headers: { authorization: `Bearer ${bearerToken}` } //Add your auth here, not needed for public APIs like petstore in this example
},
);

const options = {
method: 'POST',
headers: {
'X-CLIENT-ID': '<api-key>',
'X-CLIENT-SECRET': '<api-key>',
'Content-Type': 'application/json'
},
body: '{"clientUserId":"<string>"}'
};

fetch('https://safe.fdai.earth/api/v3/user', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
19 changes: 19 additions & 0 deletions apps/nextjs/app/api/user/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getServerSession } from "next-auth/next"
import { authOptions } from "@/lib/auth"
import {handleError} from "@/lib/errorHandler";
import { getOrCreateFdaiUser } from '@/lib/fdaiUserService';
export async function GET(
req: Request
) {
try {
const session = await getServerSession(authOptions)

if (!session?.user) {
return new Response(null, { status: 403 })
}
const fdaiUser = await getOrCreateFdaiUser(session.user.id);
return new Response(JSON.stringify(fdaiUser), { status: 200, headers: { 'Content-Type': 'application/json' } })
} catch (error) {
return handleError(error)
}
}
21 changes: 21 additions & 0 deletions apps/nextjs/app/api/userVariables/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getServerSession } from "next-auth/next"

import { authOptions } from "@/lib/auth"
import { getUserVariables } from '@/lib/fdaiUserVariablesService';
export async function GET(req: Request) {
try {
const session = await getServerSession(authOptions)

if (!session) {
return new Response("Unauthorized", { status: 403 })
}
// Parse the URL and query parameters from the request
const url = new URL(req.url, `https://${req.headers.get('host')}`);
const queryParams = Object.fromEntries(url.searchParams);
const userVariables = await getUserVariables(session.user.id, queryParams);

return new Response(JSON.stringify(userVariables))
} catch (error) {
return new Response(null, { status: 500 })
}
}
60 changes: 60 additions & 0 deletions apps/nextjs/app/api/users/[userId]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { getServerSession } from "next-auth/next"
import { z } from "zod"

import { authOptions } from "@/lib/auth"
import { db } from "@/lib/db"
import { userNameSchema } from "@/lib/validations/user"
import {handleError} from "@/lib/errorHandler";

const routeContextSchema = z.object({
params: z.object({
userId: z.string(),
}),
})

interface DatabaseError extends Error {
code: string;
meta?: {
target?: string[];
};
}

export async function PATCH(
req: Request,
context: z.infer<typeof routeContextSchema>
) {
try {
const { params } = routeContextSchema.parse(context)
const session = await getServerSession(authOptions)

if (!session?.user || params.userId !== session?.user.id) {
return new Response(null, { status: 403 })
}

// Edit username based on input
const body = await req.json()
const payload = userNameSchema.parse(body)

try {
await db.user.update({
where: {
id: session.user.id,
},
data: {
username: payload.username,
updatedAt: new Date(),
},
})
} catch (error) {
const dbError = error as DatabaseError;
if (dbError.code === 'P2002' && dbError.meta?.target?.includes('username')) {
return new Response(JSON.stringify({ message: "Username is already taken" }), { status: 409, headers: { 'Content-Type': 'application/json' } })
}
throw error;
}

return new Response(null, { status: 200 })
} catch (error) {
return handleError(error)
}
}
34 changes: 34 additions & 0 deletions apps/nextjs/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { dashboardLinks } from "@/config/links"
import { getCurrentUser } from "@/lib/session"
import Footer from "@/components/layout/footer"
import Navbar from "@/components/layout/navbar"
import { DashboardNav } from "@/components/pages/dashboard/dashboard-nav"

interface DashboardLayoutProps {
children: React.ReactNode
}

export default async function DashboardLayout({
children,
}: DashboardLayoutProps) {
const user = await getCurrentUser()

return (
<div className="flex min-h-screen flex-col space-y-6">
<Navbar
user={{
name: user?.name,
image: user?.image,
email: user?.email,
}}
/>
<div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr]">
<aside className="hidden w-[200px] flex-col md:flex">
<DashboardNav items={dashboardLinks.data} />
</aside>
<main className="flex w-full flex-1 flex-col">{children}</main>
</div>
<Footer />
</div>
)
}
12 changes: 12 additions & 0 deletions apps/nextjs/app/dashboard/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Icons } from "@/components/icons"
import { Shell } from "@/components/layout/shell"

export default async function DashboardLoading() {
return (
<Shell>
<div className="flex justify-center p-8">
<Icons.spinner className="animate-spin text-4xl" />
</div>
</Shell>
)
}
47 changes: 47 additions & 0 deletions apps/nextjs/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Metadata } from "next"
import { redirect } from "next/navigation"

import { authOptions } from "@/lib/auth"
import { getCurrentUser } from "@/lib/session"
import { ScrollArea } from "@/components/ui/scroll-area"
import { measurementColumns } from "@/components/userVariable/measurements/measurements-columns"

import { DataTable } from "@/components/data-table"
import { Shell } from "@/components/layout/shell"
import { DashboardHeader } from "@/components/pages/dashboard/dashboard-header"
import { UserVariableList } from '@/components/userVariable/user-variable-list';

export const metadata: Metadata = {
title: "Dashboard",
description: "Monitor your progress.",
}

interface DashboardProps {
searchParams: { from: string; to: string }
}

export default async function Dashboard({ searchParams }: DashboardProps) {
const user = await getCurrentUser()

if (!user) {
redirect(authOptions?.pages?.signIn || "/signin")
}


const layout = "grid grid-cols-1 gap-4 md:grid-cols-2";
const scrollClass = "h-[17rem] rounded-lg border";

return (
<Shell>
<DashboardHeader heading="Your Data" text="Monitor your symptoms and factors.">
</DashboardHeader>
<div className={layout}>
<ScrollArea className={scrollClass}>
<div className="divide-y divide-border">
<UserVariableList />
</div>
</ScrollArea>
</div>
</Shell>
)
}
Loading

0 comments on commit 92b32b9

Please sign in to comment.