Skip to content

Commit

Permalink
Implement auth, appwrite, and sentry
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianhajdin committed Apr 27, 2024
1 parent 4a5eba5 commit 44179d0
Show file tree
Hide file tree
Showing 31 changed files with 1,450 additions and 53 deletions.
24 changes: 24 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#NEXT
NEXT_PUBLIC_SITE_URL=http://localhost:3000

#APPWRITE
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT=662cd3d0000d7d095714
APPWRITE_DATABASE_ID=662cd599000b133bfcd3
APPWRITE_USER_COLLECTION_ID=662cd5bb002792396b02
APPWRITE_BANK_COLLECTION_ID=662cd610000e0e283eb4
APPWRITE_TRANSACTION_COLLECTION_ID=662cd5fb000ceb06e8a6
NEXT_APPWRITE_KEY=c78d74bfcb9364a868b588056cdb00bd7328ad2629f9d97682a877f8662776289426ab7a5dcc1ca6cb4c6946b5a341ca73cd09f18dd0c01a11454aa75cf85a3cc4b0cc5f5cbc8872368aab3d24a13ff35ca45fac080bf0d8f575d84dec72ad0446b13752978c44d379ca85bdb690b7aa0523b44d9058a6da51c474ba54aef2d7

#PLAID
PLAID_CLIENT_ID=
PLAID_SECRET=
PLAID_ENV=
PLAID_PRODUCTS=
PLAID_COUNTRY_CODES=

#DWOLLA
DWOLLA_KEY=
DWOLLA_SECRET=
DWOLLA_BASE_URL=
DWOLLA_ENV=
25 changes: 25 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#NEXT
NEXT_PUBLIC_SITE_URL=http://localhost:3000

#APPWRITE
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT=
APPWRITE_DATABASE_ID=
APPWRITE_USER_COLLECTION_ID=
APPWRITE_ITEM_COLLECTION_ID=
APPWRITE_BANK_COLLECTION_ID=
APPWRITE_TRANSACTION_COLLECTION_ID=
NEXT_APPWRITE_KEY=

#PLAID
PLAID_CLIENT_ID=
PLAID_SECRET=
PLAID_ENV=
PLAID_PRODUCTS=
PLAID_COUNTRY_CODES=

#DWOLLA
DWOLLA_KEY=
DWOLLA_SECRET=
DWOLLA_BASE_URL=
DWOLLA_ENV=
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# Sentry Config File
.sentryclirc
15 changes: 14 additions & 1 deletion app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import Image from "next/image";

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<main>
<main className="flex min-h-screen w-full justify-between font-inter">
{children}
<div className="auth-asset">
<div>
<Image
src="/icons/auth-image.svg"
alt="Auth image"
width={500}
height={500}
className="rounded-l-xl object-contain"
/>
</div>
</div>
</main>
);
}
6 changes: 4 additions & 2 deletions app/(auth)/sign-in/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react'
import AuthForm from '@/components/AuthForm'

const SignIn = () => {
return (
<div>SignIn</div>
<section className="flex-center size-full max-sm:px-6">
<AuthForm type="sign-in" />
</section>
)
}

Expand Down
8 changes: 5 additions & 3 deletions app/(auth)/sign-up/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React from 'react'
import AuthForm from '@/components/AuthForm'

const SignUp = () => {
const SignUp = async () => {
return (
<div>SignUp</div>
<section className="flex-center size-full max-sm:px-6">
<AuthForm type="sign-up" />
</section>
)
}

Expand Down
8 changes: 6 additions & 2 deletions app/(root)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import MobileNav from "@/components/MobileNav";
import Sidebar from "@/components/Sidebar";
import { getLoggedInUser } from "@/lib/actions/user.actions";
import Image from "next/image";
import { redirect } from "next/navigation";

export default function RootLayout({
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const loggedIn = { firstName: 'Adrian', lastName: 'JSM' };
const loggedIn = await getLoggedInUser();

if(!loggedIn) redirect('/sign-in')

return (
<main className="flex h-screen w-full font-inter">
Expand Down
7 changes: 4 additions & 3 deletions app/(root)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import HeaderBox from '@/components/HeaderBox'
import RightSidebar from '@/components/RightSidebar';
import TotalBalanceBox from '@/components/TotalBalanceBox';
import { getLoggedInUser } from '@/lib/actions/user.actions';

const Home = () => {
const loggedIn = { firstName: 'Adrian', lastName: 'JSM', email: '[email protected]' };
const Home = async () => {
const loggedIn = await getLoggedInUser();

return (
<section className="home">
Expand All @@ -12,7 +13,7 @@ const Home = () => {
<HeaderBox
type="greeting"
title="Welcome"
user={loggedIn?.firstName || 'Guest'}
user={loggedIn?.name || 'Guest'}
subtext="Access and manage your account and transactions efficiently."
/>

Expand Down
9 changes: 9 additions & 0 deletions app/api/sentry-example-api/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { NextResponse } from "next/server";

export const dynamic = "force-dynamic";

// A faulty API route to test Sentry's error monitoring
export function GET() {
throw new Error("Sentry Example API Route Error");
return NextResponse.json({ data: "Testing Sentry Error..." });
}
14 changes: 11 additions & 3 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

@layer utilities {
.input-class {
@apply text-16 placeholder:text-16 rounded-lg border border-gray-300 text-gray-500 placeholder:text-gray-500;
@apply text-16 placeholder:text-16 rounded-lg border border-gray-300 text-gray-900 placeholder:text-gray-500;
}

.sheet-content button {
Expand Down Expand Up @@ -155,7 +155,7 @@

/* Right sidebar */
.right-sidebar {
@apply no-scrollbar hidden h-screen max-h-screen flex-col border-l border-gray-200 xl:flex min-w-[300px] 2xl:min-w-[355px] xl:overflow-y-scroll !important;
@apply no-scrollbar hidden h-screen max-h-screen flex-col border-l border-gray-200 xl:flex w-[355px] xl:overflow-y-scroll !important;
}

.profile-banner {
Expand Down Expand Up @@ -347,13 +347,21 @@
@apply flex flex-1 flex-col justify-center max-xl:hidden;
}

.footer_name-mobile {
@apply flex size-10 items-center justify-center rounded-full bg-gray-200;
}

.footer_email-mobile {
@apply flex flex-1 flex-col justify-center;
}

.footer_image {
@apply relative size-5 max-xl:w-full max-xl:flex max-xl:justify-center max-xl:items-center;
}

/* Sidebar */
.sidebar {
@apply sticky left-0 top-0 flex h-screen w-fit flex-col justify-between border-r border-gray-200 bg-white pt-8 text-white max-md:hidden sm:p-4 xl:p-6 2xl:w-[355px];
@apply sticky left-0 top-0 flex h-screen w-fit flex-col justify-between border-r border-gray-200 bg-white pt-8 text-white max-md:hidden sm:p-4 xl:p-6 2xl:w-[355px];
}

.sidebar-logo {
Expand Down
162 changes: 162 additions & 0 deletions components/AuthForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
'use client';

import Image from 'next/image'
import Link from 'next/link'
import React, { useState } from 'react'

import { z } from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import CustomInput from './CustomInput';
import { authFormSchema } from '@/lib/utils';
import { Loader2 } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { getLoggedInUser, signIn, signUp } from '@/lib/actions/user.actions';

const AuthForm = ({ type }: { type: string }) => {
const router = useRouter();
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);

const formSchema = authFormSchema(type);

// 1. Define your form.
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: "",
password: ''
},
})

// 2. Define a submit handler.
const onSubmit = async (data: z.infer<typeof formSchema>) => {
setIsLoading(true);

try {
// Sign up with Appwrite & create plaid token

if(type === 'sign-up') {
const newUser = await signUp(data);

setUser(newUser);
}

if(type === 'sign-in') {
const response = await signIn({
email: data.email,
password: data.password,
})

if(response) router.push('/')
}
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
}

return (
<section className="auth-form">
<header className='flex flex-col gap-5 md:gap-8'>
<Link href="/" className="cursor-pointer flex items-center gap-1">
<Image
src="/icons/logo.svg"
width={34}
height={34}
alt="Horizon logo"
/>
<h1 className="text-26 font-ibm-plex-serif font-bold text-black-1">Horizon</h1>
</Link>

<div className="flex flex-col gap-1 md:gap-3">
<h1 className="text-24 lg:text-36 font-semibold text-gray-900">
{user
? 'Link Account'
: type === 'sign-in'
? 'Sign In'
: 'Sign Up'
}
<p className="text-16 font-normal text-gray-600">
{user
? 'Link your account to get started'
: 'Please enter your details'
}
</p>
</h1>
</div>
</header>
{user ? (
<div className="flex flex-col gap-4">
{/* PlaidLink */}
</div>
): (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{type === 'sign-up' && (
<>
<div className="flex gap-4">
<CustomInput control={form.control} name='firstName' label="First Name" placeholder='Enter your first name' />
<CustomInput control={form.control} name='lastName' label="Last Name" placeholder='Enter your first name' />
</div>
<CustomInput control={form.control} name='address1' label="Address" placeholder='Enter your specific address' />
<CustomInput control={form.control} name='city' label="City" placeholder='Enter your city' />
<div className="flex gap-4">
<CustomInput control={form.control} name='state' label="State" placeholder='Example: NY' />
<CustomInput control={form.control} name='postalCode' label="Postal Code" placeholder='Example: 11101' />
</div>
<div className="flex gap-4">
<CustomInput control={form.control} name='dateOfBirth' label="Date of Birth" placeholder='YYYY-MM-DD' />
<CustomInput control={form.control} name='ssn' label="SSN" placeholder='Example: 1234' />
</div>
</>
)}

<CustomInput control={form.control} name='email' label="Email" placeholder='Enter your email' />

<CustomInput control={form.control} name='password' label="Password" placeholder='Enter your password' />

<div className="flex flex-col gap-4">
<Button type="submit" disabled={isLoading} className="form-btn">
{isLoading ? (
<>
<Loader2 size={20} className="animate-spin" /> &nbsp;
Loading...
</>
) : type === 'sign-in'
? 'Sign In' : 'Sign Up'}
</Button>
</div>
</form>
</Form>

<footer className="flex justify-center gap-1">
<p className="text-14 font-normal text-gray-600">
{type === 'sign-in'
? "Don't have an account?"
: "Already have an account?"}
</p>
<Link href={type === 'sign-in' ? '/sign-up' : '/sign-in'} className="form-link">
{type === 'sign-in' ? 'Sign up' : 'Sign in'}
</Link>
</footer>
</>
)}
</section>
)
}

export default AuthForm
2 changes: 1 addition & 1 deletion components/BankCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const BankCard = ({ account, userName, showBalance = true }: CreditCardProps) =>
<div className="bank-card_content">
<div>
<h1 className="text-16 font-semibold text-white">
{account.name || userName}
{userName}
</h1>
<p className="font-ibm-plex-serif font-black text-white">
{formatAmount(account.currentBalance)}
Expand Down
Loading

0 comments on commit 44179d0

Please sign in to comment.