From 75a27678d83959a91f119238460da0a21f883540 Mon Sep 17 00:00:00 2001 From: Alberto Sierra Date: Mon, 25 Mar 2024 15:31:07 -0700 Subject: [PATCH 1/6] account and transaction nav pages --- backend/app.py | 2 + .../__pycache__/ai_chatbot.cpython-38.pyc | Bin 0 -> 1511 bytes backend/models/ai_chatbot.py | 107 +++++++++++++----- backend/seed.py | 20 ++-- frontend/app/(tabs)/_layout.js | 43 ++++--- frontend/app/(tabs)/accounts.js | 67 ++++++----- frontend/app/(tabs)/transactions.js | 43 ++++--- frontend/app/_layout.js | 43 +++---- frontend/app/accounts/AccountDetails.js | 35 ++++++ frontend/app/accounts/[id].js | 39 +++++++ frontend/app/transactions/[id].js | 16 +++ frontend/assets/icons/travel.svg | 3 + frontend/assets/icons/wallet.svg | 3 + frontend/components/LinkedAccount.js | 22 +++- frontend/components/OpenAiChat.js | 37 ++++++ frontend/components/TransactionPage.js | 15 +-- 16 files changed, 340 insertions(+), 155 deletions(-) create mode 100644 backend/models/__pycache__/ai_chatbot.cpython-38.pyc create mode 100644 frontend/app/accounts/AccountDetails.js create mode 100644 frontend/app/accounts/[id].js create mode 100644 frontend/app/transactions/[id].js create mode 100644 frontend/assets/icons/travel.svg create mode 100644 frontend/assets/icons/wallet.svg create mode 100644 frontend/components/OpenAiChat.js diff --git a/backend/app.py b/backend/app.py index cd648ce..71514ea 100644 --- a/backend/app.py +++ b/backend/app.py @@ -21,6 +21,7 @@ from routes.user_by_id import UserById from routes.account_by_id import AccountById from routes.account_by_user_id import AccountByUserId +from models.ai_chatbot import OpenAiResponse # Resources api.add_resource(Signup, '/api/signup', endpoint='/api/auth') @@ -32,6 +33,7 @@ api.add_resource(AccountById, '/api/users//accounts/') api.add_resource(AccountByUserId, '/api/users//accounts') +api.add_resource(OpenAiResponse, '/api/user//achievement') # Register a callback function that loads a user from your database whenever # a protected route is accessed. This should return any python object on a diff --git a/backend/models/__pycache__/ai_chatbot.cpython-38.pyc b/backend/models/__pycache__/ai_chatbot.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d68d59d7c5c6892b5e6cc1199e288f650c61bf5 GIT binary patch literal 1511 zcmZuxO>Z1E7#`cRv$LDcN1K!n2_zqoq7}_ zuLi+CezQD6*xZ9@egPr^fwCY+3?a|s3^QD>gb`Rt7Um7s$RifzP1dY!!Ys~Ptd+M} zyWTgl6|c)Xtb+ncp0A4N1!8M*jjc;`8L*8sVAo(?7flgA3)tpYfoO^Lvp}?E=aR53 zfgc6km6woB&_ymfE@g3eH0To7_%h?>%r#GqD$>!^HMM+NNn^X%5nh&_q+P5V;OOG- zq*0X~O1F0W@c!WNC^>w1lzeml$q@U4Uk?In6Xq8%%}pRSI7O%Uln5lSASn`|Xo%={ z>~r<&7>m}gWR7P($M)dj`na?^w|WOVw$dkRXZMr60~gw9DcOq8GEpa|a%f#-rpC(r zV*iP%DA$s5I+0m9sxr!rNsZ-&r9zr?Tu4E!qN=iGW$D+Cjt4y`FVrfNt~peNm4$U- zWu(6N?b|6WcrIyb=+E1S*@@IvZO?B~UI=5oJ?~L)7%QH62dnOY zW(<)Edd`R3rmEPUgWH6I+edr5yHx&UHK%-JrKb6m<`8_6O+gG7Vx;L&`au0i%|un1 zk7qy(xlg$L+}~BMgv{8wmZnr-o{W^vxpi?ajp1Wyx}l4PSqkN_a0pIpWvKEplRgmx zDlNH{U82|ER@@37PEvU;b6<&|z5&{Qf9OAkewaRAw7{fNYuJOF~TUq zF^*s1S5Z^H3$meCEDw*idoaxw5E(Ee0(en?E`_`H z^bRl{q0ixj7if-O^B8zC!g`vN+c**sv!2P05*@>TBNT15ZBm#Dk_z<8Ar6`0k>@ b&k&dypZ%ggTWAAqkTwoc^nQp!61V>YY-5tP literal 0 HcmV?d00001 diff --git a/backend/models/ai_chatbot.py b/backend/models/ai_chatbot.py index 4a5a144..4f42d93 100644 --- a/backend/models/ai_chatbot.py +++ b/backend/models/ai_chatbot.py @@ -1,45 +1,90 @@ from openai import OpenAI import time import os -from users import User -from achievements import Achievement +# from users import User +# from achievements import Achievement +from flask import Flask, jsonify, request +from app_setup import app, api +from flask_restful import request, Resource + # Use an environment variable for the API key OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') api_key = OPENAI_API_KEY client = OpenAI() -user = User( - first_name='Alberto', - last_name='Sierra', - email='Alberto@Sierra.com', - zipcode='94903' - ) -achievement = Achievement( - description='Go on vacation' -) +# user = User( +# first_name='Alberto', +# last_name='Sierra', +# email='Alberto@Sierra.com', +# zipcode='94903' +# ) +# achievement = Achievement( +# description='Go on vacation' +# ) # # JSON Mode for frontend -response = client.chat.completions.create( - model="gpt-3.5-turbo-0125", - response_format={ "type": "json_object" }, - messages=[ - {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, - - # {"role": "user", "content": "I have $200 extra, after my monthly expenses. I wants to invest the extra money in the S&P 500. Return the calculated amount in 10 years from now"} - - # {"role": "user", "content": "Give me a list of retirement accounts and their performance in the past 10 years in APR"} - - # {"role": "user", "content": "I have $5000 in my Roth IRA account, if i put in $200 a month, how much will it be in 30 years?"} - - # {"role": "user", "content": "Help me to create a monthly budget with $1000 in 10005. With 10 major spending categories."} - - # {"role": "user", "content": "Find what the average person per month in 94903 spends in healthcare. "} - - {"role": "user", "content": "My name is {user.first_name}, and I live in {user.zipcode}. My goal is to: {achievement.description} I have $200 extra after my monthly expenses. How should I spend that?"} - ] -) -print(response.choices[0].message.content) +# response = client.chat.completions.create( +# model="gpt-3.5-turbo-0125", +# response_format={ "type": "json_object" }, +# messages=[ +# {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, + +# {"role": "user", "content": "My name is {user.first_name}, and I live in {user.zipcode}. My goal is to: {achievement.description} I have $200 extra after my monthly expenses. How should I spend that?"} +# ] +# ) +# print(response.choices[0].message.content) + + +users = {} +achievements = {} + + +def get_openai_response(user, achievement): + response = client.chat.completions.create( + model="gpt-3.5-turbo-0125", + response_format={ "type": "json_object" }, + messages=[ + {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, + + {"role": "user", "content": "My name is {'Alberto'}, and I live in {'94903'}. My goal is to: {'go on vacation'} I have $200 extra after my monthly expenses. How should I spend that?"} ]) + return response + +class OpenAiResponse(Resource): + def post(self, user_id): + + user_details = request.json.get('user') + achievement_details = request.json.get('achievement') + users[user_id] = user_details + achievements[user_id] = achievement_details + + # Get response from OpenAI (simulated here) + openai_response = get_openai_response(user_details, achievement_details) + + # Return the OpenAI response as JSON + return openai_response, 200 + + + + + + + + + + + + + + + + + + + + + + #Chat-bot instructions and model # assistant = client.beta.assistants.create( diff --git a/backend/seed.py b/backend/seed.py index 3dbd8b3..68225cf 100644 --- a/backend/seed.py +++ b/backend/seed.py @@ -44,40 +44,40 @@ # Create accounts print('Creating accounts...') account1 = Account( - id='QlRwmeljv1fxg3aolDwXSe4Kqwv3RjFwmvbzN5', + id='1', name='Bank3', - balance=10, + balance=10.00, type='Checking', currency='GBP', user_id=user1.id ) account2 = Account( - id='QlRwmeljv1fxg3aolDwXSe4Kqwv3RjFwmvbzN6', + id='2', name='Bank4', - balance=70, + balance=70.00, type='Credit', currency='GBP', - limit=300, + limit=300.0, user_id=user1.id ) account3 = Account( - id='QlRwmeljv1fxg3aolDwXSe4Kqwv3RjFwmvbzN3', + id='3', name='Bank3', - balance=10, + balance=10.00, type='Checking', currency='GBP', user_id=user2.id ) account4 = Account( - id='QlRwmeljv1fxg3aolDwXSe4Kqwv3RjFwmvbzN4', + id='4', name='Bank4', - balance=70, + balance=70.00, type='Credit', currency='GBP', - limit=300, + limit=300.0, user_id=user2.id ) diff --git a/frontend/app/(tabs)/_layout.js b/frontend/app/(tabs)/_layout.js index 39864a6..aa94072 100644 --- a/frontend/app/(tabs)/_layout.js +++ b/frontend/app/(tabs)/_layout.js @@ -1,53 +1,52 @@ -import { useContext } from 'react' -import { Redirect, Tabs } from 'expo-router' -import { GlobalState } from '../../global-provider' -import IconDashboard from '../../assets/icons/dashboard.svg' -import IconAccounts from '../../assets/icons/accounts.svg' -import IconBudget from '../../assets/icons/budget.svg' -import IconTransactions from '../../assets/icons/transactions.svg' -import IconInvestments from '../../assets/icons/investments.svg' +import { useContext } from "react"; +import { Redirect, Tabs } from "expo-router"; +import { GlobalState } from "../../global-provider"; +import IconDashboard from "../../assets/icons/dashboard.svg"; +import IconAccounts from "../../assets/icons/accounts.svg"; +import IconBudget from "../../assets/icons/budget.svg"; +import IconTransactions from "../../assets/icons/transactions.svg"; +import IconInvestments from "../../assets/icons/investments.svg"; export default function TabLayout() { - const global = useContext(GlobalState) - + const global = useContext(GlobalState); return ( - + , }} /> , }} /> , }} /> , }} /> , }} /> - ) + ); } diff --git a/frontend/app/(tabs)/accounts.js b/frontend/app/(tabs)/accounts.js index 7bb9cbb..f3abe26 100644 --- a/frontend/app/(tabs)/accounts.js +++ b/frontend/app/(tabs)/accounts.js @@ -1,41 +1,40 @@ -import { View, Text } from "react-native"; +import { View, Text, ScrollView } from "react-native"; +import { useState, useEffect } from "react"; +import { useNavigation } from "@react-navigation/native"; +import OpenAiChat from "../../components/OpenAiChat"; import LinkedAccount from "../../components/LinkedAccount"; export default function Tab() { - const account1_info = { - name: "Cash 2", - desc: "Selling income", - balance: "560 euros", - }; - const account2_info = { - name: "Credit card", - desc: "Discover Card", - balance: "5,000 euros", - }; - const account3_info = { - name: "Fidelity", - desc: "Retirement fund", - balance: "20,000 euros", - }; + const [accountDetails, setAccountDetails] = useState(""); - return ( - - Manual account - - Linked Credit cards - + useEffect(() => { + const userId = "1"; + const accountId = "1"; + + const url = `http://127.0.0.1:5555/api/users/${userId}/accounts/${accountId}`; - - Linked Investment accounts - - - + // Fetch account details from your API + fetch(url) + .then((response) => response.json()) + .then((data) => { + setAccountDetails(data); + }) + .catch((error) => + console.error("Error fetching account details:", error) + ); + }, []); + + return ( + + Manual account + {accountDetails.length > 0 ? ( + accountDetails.map((account, index) => ( + + )) + ) : ( + No accounts found. + )} + {/* */} + ); } diff --git a/frontend/app/(tabs)/transactions.js b/frontend/app/(tabs)/transactions.js index 7b1f9c5..fac6d86 100644 --- a/frontend/app/(tabs)/transactions.js +++ b/frontend/app/(tabs)/transactions.js @@ -8,6 +8,7 @@ import { } from "react-native"; import { Card, Paragraph } from "react-native-paper"; import ReceiptScanner from "../../components/ReceiptScanner"; +import { Link } from "expo-router"; export default function Tab() { const [transactions, setTransactions] = useState([]); @@ -46,28 +47,26 @@ export default function Tab() { (isLoading ? ( ) : ( - item.id.toString()} - contentContainerStyle={styles.flatListContent} - renderItem={({ item }) => ( - - navigation.navigate("TransactionDetail", { - transaction: item, - }) - } - > - - - Date: {item.date} - Amount: {item.amount} - Category: {item.category} - - - - )} - /> + + + + + Date: + Amount: + Category: + + + + + + + Date: + Amount: + Category: + + + + ))} ); diff --git a/frontend/app/_layout.js b/frontend/app/_layout.js index 809234e..b40c8aa 100644 --- a/frontend/app/_layout.js +++ b/frontend/app/_layout.js @@ -1,15 +1,16 @@ -import { useEffect } from 'react' -import { SplashScreen, Slot, Stack } from 'expo-router' -import { SafeAreaProvider } from 'react-native-safe-area-context' -import { PaperProvider } from 'react-native-paper' -import { DispatchProvider } from '../global-provider' +import { useEffect } from "react"; +import { SplashScreen, Slot, Stack } from "expo-router"; +import { SafeAreaProvider } from "react-native-safe-area-context"; +import { PaperProvider } from "react-native-paper"; +import { DispatchProvider } from "../global-provider"; +import AccountDetails from "./accounts/AccountDetails"; -SplashScreen.preventAutoHideAsync() +SplashScreen.preventAutoHideAsync(); export default function AppLayout() { useEffect(() => { - SplashScreen.hideAsync() - }, []) + SplashScreen.hideAsync(); + }, []); return ( @@ -17,32 +18,24 @@ export default function AppLayout() { + - - - + + + - ) + ); } diff --git a/frontend/app/accounts/AccountDetails.js b/frontend/app/accounts/AccountDetails.js new file mode 100644 index 0000000..539855b --- /dev/null +++ b/frontend/app/accounts/AccountDetails.js @@ -0,0 +1,35 @@ +import React from "react"; +import { View, Text } from "react-native"; +import IconCoins from "../../assets/icons/coins.svg"; +import IconTravel from "../../assets/icons/travel.svg"; +import IconWallet from "../../assets/icons/wallet.svg"; + +const AccountDetails = ({ route }) => { + const accountInfo = { + id: "1", + name: "Cash 2", + desc: "Selling income", + balance: "560 euros", + }; + return ( + + + Balance: {accountInfo.balance} + + + Name: {accountInfo.name} + + + Your recent transactions + + + Travel + 03-23-24 + + + + + ); +}; + +export default AccountDetails; diff --git a/frontend/app/accounts/[id].js b/frontend/app/accounts/[id].js new file mode 100644 index 0000000..46b888a --- /dev/null +++ b/frontend/app/accounts/[id].js @@ -0,0 +1,39 @@ +import { StyleSheet, Text, View } from "react-native"; +import React from "react"; +import { useLocalSearchParams } from "expo-router"; +import LinkedAccount from "../../components/LinkedAccount"; +import AccountDetails from "./AccountDetails"; +import IconCoins from "../../assets/icons/coins.svg"; +import IconTravel from "../../assets/icons/travel.svg"; +import IconWallet from "../../assets/icons/wallet.svg"; + +const AccountPage = ({ account_info }) => { + const { id, name, desc, balance } = useLocalSearchParams(); + + return ( + + + AccountPage - {id} {name} + + + Balance: {balance} + + + Name: {name} + + + Your recent transactions + + + {desc} + 03-23-24 + + + + + ); +}; + +export default AccountPage; + +const styles = StyleSheet.create({}); diff --git a/frontend/app/transactions/[id].js b/frontend/app/transactions/[id].js new file mode 100644 index 0000000..0741cdb --- /dev/null +++ b/frontend/app/transactions/[id].js @@ -0,0 +1,16 @@ +import { StyleSheet, Text, View } from "react-native"; +import React from "react"; +import { useLocalSearchParams } from "expo-router"; + +const TransactionsPage = () => { + const { id } = useLocalSearchParams(); + return ( + + TransactionsPage - {id} + + ); +}; + +export default TransactionsPage; + +const styles = StyleSheet.create({}); diff --git a/frontend/assets/icons/travel.svg b/frontend/assets/icons/travel.svg new file mode 100644 index 0000000..7af10fa --- /dev/null +++ b/frontend/assets/icons/travel.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/assets/icons/wallet.svg b/frontend/assets/icons/wallet.svg new file mode 100644 index 0000000..a586f08 --- /dev/null +++ b/frontend/assets/icons/wallet.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/components/LinkedAccount.js b/frontend/components/LinkedAccount.js index c33f550..a5cf4ab 100644 --- a/frontend/components/LinkedAccount.js +++ b/frontend/components/LinkedAccount.js @@ -1,7 +1,9 @@ -import { StyleSheet, Text, View } from "react-native"; +import { Pressable, StyleSheet, Text, View } from "react-native"; import React from "react"; import IconCoins from "../assets/icons/coins.svg"; +import { Link, router } from "expo-router"; + const LinkedAccount = ({ account_info }) => { return ( @@ -13,6 +15,24 @@ const LinkedAccount = ({ account_info }) => { {account_info.balance} + + router.push({ + pathname: `/accounts/${account_info.id}/`, + params: { + id: account_info.id, + name: account_info.name, + type: account_info.type || null, + limit: account_info.limit || null, + currency: account_info.currency || null, + desc: account_info.desc, + balance: account_info.balance, + }, + }) + } + > + Click to view account details + ); }; diff --git a/frontend/components/OpenAiChat.js b/frontend/components/OpenAiChat.js new file mode 100644 index 0000000..9e5c366 --- /dev/null +++ b/frontend/components/OpenAiChat.js @@ -0,0 +1,37 @@ +import { StyleSheet, Text, View } from "react-native"; +import React, { useEffect, useState } from "react"; + +const OpenAiChat = () => { + const [response, setResponse] = useState(""); + + useEffect(() => { + const userData = { + user: {}, + achievement: {}, + }; + fetch("http://127.0.0.1:5555/user/1/achievement", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(userData), + }) + .then((response) => response.json()) + .then((data) => { + setResponse(data.message); + }) + .catch((error) => { + console.error("Error:", error); + }); + }, []); + console.log(response); + return ( + + {response} + + ); +}; + +export default OpenAiChat; + +const styles = StyleSheet.create({}); diff --git a/frontend/components/TransactionPage.js b/frontend/components/TransactionPage.js index af9bacd..755f4cf 100644 --- a/frontend/components/TransactionPage.js +++ b/frontend/components/TransactionPage.js @@ -8,6 +8,7 @@ import { } from "react-native"; import { Card, Paragraph } from "react-native-paper"; import ReceiptScanner from "./ReceiptScanner"; +import { Link } from "expo-router"; const TransactionPage = ({ navigation }) => { // Ensure navigation is received here @@ -52,21 +53,15 @@ const TransactionPage = ({ navigation }) => { keyExtractor={(item) => item.id.toString()} contentContainerStyle={styles.flatListContent} renderItem={({ item }) => ( - - navigation.navigate("TransactionDetail", { - transaction: item, - }) - } - > - + + Date: {item.date} Amount: {item.amount} Category: {item.category} - - + + )} /> ))} From 25bbb5b343b9271e4ca9b9dd7bb77df546fd16ee Mon Sep 17 00:00:00 2001 From: Alberto Sierra Date: Mon, 25 Mar 2024 22:13:34 -0700 Subject: [PATCH 2/6] openai on frontend --- backend/app.py | 5 +- .../__pycache__/ai_chatbot.cpython-38.pyc | Bin 1511 -> 1435 bytes backend/models/ai_chatbot.py | 99 ++---------------- backend/seed.py | 6 +- frontend/app/(tabs)/accounts.js | 20 ++-- frontend/app/(tabs)/dashboard.js | 11 +- frontend/app/accounts/AccountDetails.js | 2 +- frontend/app/accounts/[id].js | 20 ++-- frontend/app/index.js | 42 ++++---- frontend/components/HomeScreen.js | 4 +- frontend/components/LinkedAccount.js | 36 +++---- frontend/components/OpenAiChat.js | 52 +++++---- 12 files changed, 114 insertions(+), 183 deletions(-) diff --git a/backend/app.py b/backend/app.py index 71514ea..2a348eb 100644 --- a/backend/app.py +++ b/backend/app.py @@ -21,7 +21,7 @@ from routes.user_by_id import UserById from routes.account_by_id import AccountById from routes.account_by_user_id import AccountByUserId -from models.ai_chatbot import OpenAiResponse +from models.ai_chatbot import GetResponse # Resources api.add_resource(Signup, '/api/signup', endpoint='/api/auth') @@ -32,8 +32,9 @@ api.add_resource(UserById, '/api/users/') api.add_resource(AccountById, '/api/users//accounts/') api.add_resource(AccountByUserId, '/api/users//accounts') +api.add_resource(GetResponse, '/api/get-response') -api.add_resource(OpenAiResponse, '/api/user//achievement') +# api.add_resource(OpenAiResponse, '/api/user//achievement') # Register a callback function that loads a user from your database whenever # a protected route is accessed. This should return any python object on a diff --git a/backend/models/__pycache__/ai_chatbot.cpython-38.pyc b/backend/models/__pycache__/ai_chatbot.cpython-38.pyc index 8d68d59d7c5c6892b5e6cc1199e288f650c61bf5..82b510eed13cb56142b248eff45e368c3cdd16e6 100644 GIT binary patch delta 577 zcmZut&1w`u5bmGd*>Pq^XE&QjG>3*9mLVYtBpwoiC?a7G28{|b9%PcPoea#*#O_YO zpm^B4K$xR>0DS`=LG%sg;=zOX7FKT(+=B)6QPuT*#aI2?|Ku;d3<8f}JRY#&g+s_M z+e}9jn+KTs;(RIHlC47;cchEhl{ch!M&cRhNgrmQ1KlwjGfH-dY|G$`$N<`79?wel zoJ2RzzrO0kt%vzOC{xCc0+T7ILi&BXdh$*b$p}QI#nrsOERsTsfylEHd@n?Qb7OOD zy}u%mo0ds#xu)C_)dKr`H};c*!wgPf1cj+x1^TEgG<+edSN&vf>0I#1EqrU@Y?}~gtaeN&N3y%2+Q+E*zG1TC3ALx53>Chg;cK>|CNyL?1TsMz3 zC|$Rwoal(v4GsA)4zDe}8G+VG3aW>i{4X)|K#d6wyT)hI(O-$kLIeyrg}YZnRhH)`aDuuVJ3wCZK~bvQ7MmexNrYMTV0#F V)a5-I(vW+sL7k369qxJ|`wQxzf8GE9 delta 653 zcmZut&ubGw6rNvc*UhH3rcsLub26+cRnQ2nMXVmgg9=qa_7c|4ByQR4#+hkAyP=2n z;I%9W;ze)zpNNWoz#P1I5dR7BO>88B58gN5oA=(wd+$y4N452+R`U_q!_T<;t%lHd z7fc(2;0CyUdD!ZmPf)Y~enQL&?*K*h+ejP}{s9uc4EAZXD98g8Ru8{CF5CFi&DVFI z^DOR5o@l?=|Ykar7M&6&X z)p+Y^B8RdsGh;m^^&ro*{D4%W{6(JORym+c>=X=HzM}2Dx%&f|Z6upVWm^Oi6RQBB z;J3i_8h}CD_$?9m9UhS*PJq*b7+T-f(hDnYqEQ44ir=!;O9qx za7Kat94UjOQmfvwSiV{)UaZimTA|{x{6LBqF_vfG;xyZs6N%vnS374 zVYN8vOirL0Zh$eI1ZLSF*B8uHpgI6RB)|dout$CRqW { const userId = "1"; @@ -15,9 +15,15 @@ export default function Tab() { // Fetch account details from your API fetch(url) - .then((response) => response.json()) + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) .then((data) => { setAccountDetails(data); + console.log("Data: ", data); }) .catch((error) => console.error("Error fetching account details:", error) @@ -27,14 +33,12 @@ export default function Tab() { return ( Manual account - {accountDetails.length > 0 ? ( - accountDetails.map((account, index) => ( - - )) + {accountDetails ? ( + ) : ( - No accounts found. + No account details found. )} - {/* */} + ); } diff --git a/frontend/app/(tabs)/dashboard.js b/frontend/app/(tabs)/dashboard.js index 07d429c..86fb2fe 100644 --- a/frontend/app/(tabs)/dashboard.js +++ b/frontend/app/(tabs)/dashboard.js @@ -1,9 +1,12 @@ -import { View, Text } from 'react-native' +import { View, Text } from "react-native"; +import OpenAiChat from "../../components/OpenAiChat"; export default function Tab() { return ( - - Tab [Home|Settings] + + + + - ) + ); } diff --git a/frontend/app/accounts/AccountDetails.js b/frontend/app/accounts/AccountDetails.js index 539855b..f348ff7 100644 --- a/frontend/app/accounts/AccountDetails.js +++ b/frontend/app/accounts/AccountDetails.js @@ -13,7 +13,7 @@ const AccountDetails = ({ route }) => { }; return ( - + Balance: {accountInfo.balance} diff --git a/frontend/app/accounts/[id].js b/frontend/app/accounts/[id].js index 46b888a..c2338ab 100644 --- a/frontend/app/accounts/[id].js +++ b/frontend/app/accounts/[id].js @@ -7,26 +7,24 @@ import IconCoins from "../../assets/icons/coins.svg"; import IconTravel from "../../assets/icons/travel.svg"; import IconWallet from "../../assets/icons/wallet.svg"; -const AccountPage = ({ account_info }) => { - const { id, name, desc, balance } = useLocalSearchParams(); +const AccountPage = ({}) => { + const { id, name, type, limit, currency, balance } = useLocalSearchParams(); return ( - - - AccountPage - {id} {name} - - - Balance: {balance} + + + Name: {name} + Balance: ${balance} + {currency} + {type} + {limit} - Name: {name} Your recent transactions - {desc} - 03-23-24 diff --git a/frontend/app/index.js b/frontend/app/index.js index e83e0c5..97e99ce 100644 --- a/frontend/app/index.js +++ b/frontend/app/index.js @@ -1,41 +1,41 @@ -import { useContext } from 'react' -import { View, Image } from 'react-native' -import { Button } from 'react-native-paper' -import { router } from 'expo-router' -import { GlobalState } from '../global-provider' -import { SafeAreaView } from 'react-native-safe-area-context' -import { Redirect } from 'expo-router' +import { useContext } from "react"; +import { View, Image } from "react-native"; +import { Button } from "react-native-paper"; +import { router } from "expo-router"; +import { GlobalState } from "../global-provider"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { Redirect } from "expo-router"; export default function App() { - const global = useContext(GlobalState) + const global = useContext(GlobalState); //change true to false to imitate logged in state if (global.state.isLoggedIn === true) { - return + return ; } return ( - - + + console.log(e.nativeEvent.error)} // Log image loading errors /> - ) + ); } diff --git a/frontend/components/HomeScreen.js b/frontend/components/HomeScreen.js index 30eb40e..e703d4d 100644 --- a/frontend/components/HomeScreen.js +++ b/frontend/components/HomeScreen.js @@ -4,11 +4,11 @@ import { TextInput, Button, Text, Snackbar } from "react-native-paper"; const HomeScreen = ({ navigation }) => { return ( - + console.log(e.nativeEvent.error)} // Log image loading errors /> diff --git a/frontend/components/LinkedAccount.js b/frontend/components/LinkedAccount.js index a5cf4ab..e1cbe60 100644 --- a/frontend/components/LinkedAccount.js +++ b/frontend/components/LinkedAccount.js @@ -4,34 +4,34 @@ import IconCoins from "../assets/icons/coins.svg"; import { Link, router } from "expo-router"; -const LinkedAccount = ({ account_info }) => { +const LinkedAccount = ({ account }) => { + console.log("Name: ", account); return ( - - - - {account_info.name} - {account_info.desc} - - {account_info.balance} - router.push({ - pathname: `/accounts/${account_info.id}/`, + pathname: `/accounts/${account.id}/`, params: { - id: account_info.id, - name: account_info.name, - type: account_info.type || null, - limit: account_info.limit || null, - currency: account_info.currency || null, - desc: account_info.desc, - balance: account_info.balance, + id: account.id, + name: account.name, + type: account.type || null, + limit: account.limit || null, + currency: account.currency || null, + balance: account.balance, }, }) } > - Click to view account details + + + + {account.name} + {account.currency} + + ${account.balance} + ${account.type} + ); diff --git a/frontend/components/OpenAiChat.js b/frontend/components/OpenAiChat.js index 9e5c366..eceeec6 100644 --- a/frontend/components/OpenAiChat.js +++ b/frontend/components/OpenAiChat.js @@ -1,37 +1,43 @@ -import { StyleSheet, Text, View } from "react-native"; import React, { useEffect, useState } from "react"; +import { View, Text, StyleSheet } from "react-native"; const OpenAiChat = () => { - const [response, setResponse] = useState(""); + const [aiResponse, setAiResponse] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); // To handle errors useEffect(() => { - const userData = { - user: {}, - achievement: {}, + const fetchData = async () => { + try { + const response = await fetch("http://127.0.0.1:5555/api/get-response", { + headers: { "Content-Type": "application/json" }, + }); + const data = await response.json(); + console.log("DATA: ", data); + setAiResponse(`Ai Data: ${data || "No response"}`); + } catch (error) { + console.error("Error fetching AI response:", error); + setErrorMessage("Failed to fetch AI response. Please try again later."); + } }; - fetch("http://127.0.0.1:5555/user/1/achievement", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(userData), - }) - .then((response) => response.json()) - .then((data) => { - setResponse(data.message); - }) - .catch((error) => { - console.error("Error:", error); - }); + + fetchData(); }, []); - console.log(response); + console.log("AI Response:", aiResponse); return ( - - {response} + + OpenAI Chat Response: + {aiResponse} + {errorMessage ? ( + {errorMessage} + ) : null} ); }; export default OpenAiChat; -const styles = StyleSheet.create({}); +const styles = StyleSheet.create({ + container: { padding: 20 }, + responseText: { marginTop: 10 }, + errorText: { marginTop: 10, color: "red" }, +}); From c3bc364b3cbc6a09d4425dab3b5c326605594a2d Mon Sep 17 00:00:00 2001 From: Alberto Sierra Date: Tue, 26 Mar 2024 00:10:02 -0700 Subject: [PATCH 3/6] openai json frontend upgrade --- backend/models/ai_chatbot.py | 40 ++++++++------------ frontend/assets/icons/openai-svgrepo-com.svg | 2 + frontend/components/OpenAiChat.js | 31 ++++++++------- frontend/tailwind.config.js | 19 +++++----- 4 files changed, 46 insertions(+), 46 deletions(-) create mode 100644 frontend/assets/icons/openai-svgrepo-com.svg diff --git a/backend/models/ai_chatbot.py b/backend/models/ai_chatbot.py index 2209b68..686849f 100644 --- a/backend/models/ai_chatbot.py +++ b/backend/models/ai_chatbot.py @@ -1,45 +1,37 @@ from openai import OpenAI -import time import os -# from users import User -# from achievements import Achievement -from flask import Flask, jsonify, request -# from app_setup import app, api from flask_restful import request, Resource -# Use an environment variable for the API key OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') api_key = OPENAI_API_KEY client = OpenAI() - -# # JSON Mode for frontend -# response = client.chat.completions.create( -# model="gpt-3.5-turbo-0125", -# response_format={ "type": "json_object" }, -# messages=[ -# {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, - -# {"role": "user", "content": "My name is {user.first_name}, and I live in {user.zipcode}. My goal is to: {achievement.description} I have $200 extra after my monthly expenses. How should I spend that?"} -# ] -# ) -# print(response.choices[0].message.content) - - - users = {'Alberto'} achievements = {"retire"} +# json_schema = { +# "name": "Alberto", +# "location": "94903", +# "goal": "go on vacation", +# "extraIncome": 200, +# "suggestions": [ +# "Save the extra money for your vacation fund", +# "Consider putting the extra money towards booking a flight or accommodation for your vacation", +# "Use the extra money for activities and experiences during your vacation" +# ] +# } + def get_openai_response(user, achievement): response = client.chat.completions.create( - model="gpt-3.5-turbo-0125", - response_format={ "type": "json_object" }, + model="gpt-3.5-turbo", + # response_format={ "type": "json_object" }, messages=[ {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, - {"role": "user", "content": "My name is Alberto', and I live in '94903'. My goal is to: 'go on vacation'} I have $200 extra after my monthly expenses. How should I spend that?"} ]) + {"role": "user", "content": f"My name is Alberto', and I live in '94903'. My goal is to: 'go on vacation' I have $200 extra after my monthly expenses. How should I spend that? "} ]) + return response.choices[0].message.content diff --git a/frontend/assets/icons/openai-svgrepo-com.svg b/frontend/assets/icons/openai-svgrepo-com.svg new file mode 100644 index 0000000..5b697fe --- /dev/null +++ b/frontend/assets/icons/openai-svgrepo-com.svg @@ -0,0 +1,2 @@ + +OpenAI icon \ No newline at end of file diff --git a/frontend/components/OpenAiChat.js b/frontend/components/OpenAiChat.js index eceeec6..20a66d8 100644 --- a/frontend/components/OpenAiChat.js +++ b/frontend/components/OpenAiChat.js @@ -1,8 +1,8 @@ import React, { useEffect, useState } from "react"; -import { View, Text, StyleSheet } from "react-native"; - +import { View, Text, StyleSheet, ScrollView } from "react-native"; +import OpenAiIcon from "../assets/icons/openai-svgrepo-com.svg"; const OpenAiChat = () => { - const [aiResponse, setAiResponse] = useState(""); + const [aiResponse, setAiResponse] = useState(); // Default suggestions to empty array const [errorMessage, setErrorMessage] = useState(""); // To handle errors useEffect(() => { @@ -12,8 +12,7 @@ const OpenAiChat = () => { headers: { "Content-Type": "application/json" }, }); const data = await response.json(); - console.log("DATA: ", data); - setAiResponse(`Ai Data: ${data || "No response"}`); + setAiResponse(` ${data || "No response"}`); } catch (error) { console.error("Error fetching AI response:", error); setErrorMessage("Failed to fetch AI response. Please try again later."); @@ -22,15 +21,21 @@ const OpenAiChat = () => { fetchData(); }, []); - console.log("AI Response:", aiResponse); + return ( - - OpenAI Chat Response: - {aiResponse} - {errorMessage ? ( - {errorMessage} - ) : null} - + + + Powered by OpenAI + + {aiResponse} + + + + {/* Powered by OpenAI */} + + + + ); }; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index c81b05d..7bba71d 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,20 +1,21 @@ /** @type {import('tailwindcss').Config} */ module.exports = { content: [ - './app/**/*.{js,jsx,ts,tsx}', // covering expo-router - './components/**/*.{js,jsx,ts,tsx}', // For component files + "./app/**/*.{js,jsx,ts,tsx}", // covering expo-router + "./components/**/*.{js,jsx,ts,tsx}", // For component files ], theme: { extend: {}, colors: { - 'magnetic-grey': '#eae4ef', - 'mint-green': '#c0ffd0', - 'money-green': '#009933', - 'forest-green': '#00661F', - 'magnetic-plum': '#32004c', - 'error-red': '#DC2626', + "magnetic-grey": "#eae4ef", + white: "#fff", + "mint-green": "#c0ffd0", + "money-green": "#009933", + "forest-green": "#00661F", + "magnetic-plum": "#32004c", + "error-red": "#DC2626", }, }, plugins: [], -} +}; From 9efad97786be259a9c5a30eb233a992d062d81f1 Mon Sep 17 00:00:00 2001 From: Alberto Sierra Date: Tue, 26 Mar 2024 02:16:18 -0700 Subject: [PATCH 4/6] cleaned up and upgraded Accounts --- backend/models/ai_chatbot.py | 7 +- frontend/app/(tabs)/accounts.js | 16 +- frontend/app/accounts/AddNewAccount.js | 196 +++++++++++++++ frontend/app/accounts/RecentTransactions.js | 253 ++++++++++++++++++++ frontend/app/accounts/VisaCard.js | 15 ++ frontend/app/accounts/[id].js | 15 +- frontend/assets/icons/transaction-item.svg | 10 + frontend/assets/icons/visa.svg | 33 +++ frontend/components/OpenAiChat.js | 12 +- frontend/package-lock.json | 71 ++++++ frontend/package.json | 2 + 11 files changed, 608 insertions(+), 22 deletions(-) create mode 100644 frontend/app/accounts/AddNewAccount.js create mode 100644 frontend/app/accounts/RecentTransactions.js create mode 100644 frontend/app/accounts/VisaCard.js create mode 100644 frontend/assets/icons/transaction-item.svg create mode 100644 frontend/assets/icons/visa.svg diff --git a/backend/models/ai_chatbot.py b/backend/models/ai_chatbot.py index 686849f..69c449d 100644 --- a/backend/models/ai_chatbot.py +++ b/backend/models/ai_chatbot.py @@ -25,13 +25,12 @@ def get_openai_response(user, achievement): response = client.chat.completions.create( - model="gpt-3.5-turbo", - # response_format={ "type": "json_object" }, + model="gpt-3.5-turbo-0125", + response_format={ "type": "json_object" }, messages=[ - {"role": "system", "content": "You are a helpful assistant designed to output JSON."}, + {"role": "system", "content": "You are a helpful assistant, return JSON."}, {"role": "user", "content": f"My name is Alberto', and I live in '94903'. My goal is to: 'go on vacation' I have $200 extra after my monthly expenses. How should I spend that? "} ]) - return response.choices[0].message.content diff --git a/frontend/app/(tabs)/accounts.js b/frontend/app/(tabs)/accounts.js index 0332922..d8e2db2 100644 --- a/frontend/app/(tabs)/accounts.js +++ b/frontend/app/(tabs)/accounts.js @@ -1,8 +1,10 @@ -import { View, Text, ScrollView } from "react-native"; +import { View, Text, ScrollView, Pressable } from "react-native"; import { useState, useEffect } from "react"; import { useNavigation } from "@react-navigation/native"; import OpenAiChat from "../../components/OpenAiChat"; import LinkedAccount from "../../components/LinkedAccount"; +import { Button } from "react-native-paper"; +import { Link, router } from "expo-router"; export default function Tab() { const [accountDetails, setAccountDetails] = useState(null); @@ -38,7 +40,17 @@ export default function Tab() { ) : ( No account details found. )} - + + router.push({ + pathname: `/accounts/AddNewAccount/`, + }) + } + > + + ); } diff --git a/frontend/app/accounts/AddNewAccount.js b/frontend/app/accounts/AddNewAccount.js new file mode 100644 index 0000000..e14d2fe --- /dev/null +++ b/frontend/app/accounts/AddNewAccount.js @@ -0,0 +1,196 @@ +import * as React from "react"; +import { StyleSheet, View } from "react-native"; +import { Formik } from "formik"; +import * as Yup from "yup"; +import { TextInput, Button, Text } from "react-native-paper"; + +// Yup schema for validation +const AccountSchema = Yup.object().shape({ + accountName: Yup.string().required("Account name is required"), + accountType: Yup.string().required("Account type is required"), +}); + +const AddNewAccount = () => { + const [selectedValue, setSelectedValue] = React.useState(""); + + const handleOnChange = (value) => { + setSelectedValue(value); + }; + + return ( + + Add an Account + console.log(values)} + > + {({ + handleChange, + handleBlur, + handleSubmit, + values, + errors, + touched, + }) => ( + + + + + {errors.accountName && touched.accountName ? ( + {errors.accountName} + ) : null} + + + + + {errors.accountType && touched.accountType ? ( + {errors.accountType} + ) : null} + + + + )} + + + ); +}; +const styles = StyleSheet.create({ + addAnAccount: { + fontSize: 24, + lineHeight: 19, + fontFamily: "Staatliches-Regular", + color: "#32004c", + textAlign: "center", + marginTop: 20, + paddingTop: 15, + }, + inputLayout: { + paddingVertical: 6, + height: 46, + borderWidth: 0.8, + borderColor: "#cfd3d4", + borderStyle: "solid", + borderRadius: 6, + paddingHorizontal: 13, + width: 296, + }, + inputContentFlexBox: { + justifyContent: "space-between", + alignSelf: "stretch", + flex: 1, + }, + labelTypo: { + color: "#5e6366", + fontSize: 12, + textAlign: "left", + }, + inputContentFlexBox1: { + flexDirection: "row", + alignItems: "center", + }, + label1Typo: { + fontSize: 16, + fontFamily: "Inter-Regular", + }, + addAnAccount: { + fontSize: 24, + lineHeight: 19, + fontFamily: "Staatliches-Regular", + color: "#32004c", + textAlign: "center", + }, + accountName: { + lineHeight: 10, + fontFamily: "Rubik-Regular", + width: 93, + textAlign: "left", + }, + accountName1: { + fontSize: 13, + textAlign: "right", + width: 115, + color: "#abafb1", + fontFamily: "Inter-Regular", + }, + inputContent: { + flexDirection: "row", + alignItems: "center", + }, + input: { + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + }, + label: { + fontFamily: "Inter-Regular", + textAlign: "left", + alignSelf: "stretch", + }, + placehoder: { + color: "#abafb1", + textAlign: "left", + alignSelf: "stretch", + }, + fichevronDownIcon: { + width: 19, + height: 19, + overflow: "hidden", + marginLeft: 12.63, + }, + inputContent1: { + alignSelf: "stretch", + flex: 1, + }, + select: { + marginTop: 18.95, + }, + label1: { + color: "#fff", + textAlign: "center", + flex: 1, + }, + component4: { + borderRadius: 9, + backgroundColor: "#009933", + paddingVertical: 13, + marginTop: 34.74, + paddingHorizontal: 13, + width: 296, + flexDirection: "row", + justifyContent: "center", + alignItems: "center", + }, + inputcontainer: { + marginTop: 31.58, + }, + innercontainer: { + width: "100%", + paddingHorizontal: 0, + paddingVertical: 58, + justifyContent: "center", + alignItems: "center", + flex: 1, + }, +}); + +export default AddNewAccount; diff --git a/frontend/app/accounts/RecentTransactions.js b/frontend/app/accounts/RecentTransactions.js new file mode 100644 index 0000000..90538be --- /dev/null +++ b/frontend/app/accounts/RecentTransactions.js @@ -0,0 +1,253 @@ +import * as React from "react"; +import { Text, StyleSheet, Image, View } from "react-native"; +import IconTravel from "../../assets/icons/travel.svg"; +import IconWallet from "../../assets/icons/wallet.svg"; +import IconTransaction from "../../assets/icons/transaction-item.svg"; + +const RecentTransaction = () => { + return ( + + + Recent Transactions + + + + + + + + Travel + 16-11-23 + + -5000 + + + + + + + + {/* */} + Shopping + 16-11-23 + + -5000 + + + + + + + + + Electronics + 16-11-23 + -5000 + + + + + + + + + + Electronics + 16-11-23 + -5000 + + + + + ); +}; + +const styles = StyleSheet.create({ + header1ChildLayout: { + overflow: "hidden", + width: "100%", + }, + transactionItemSpaceBlock: { + paddingVertical: 6, + paddingHorizontal: 10, + justifyContent: "center", + alignItems: "center", + width: 393, + backgroundColor: "#fdfdfe", + left: 0, + position: "absolute", + overflow: "hidden", + }, + outlineIconLayout: { + height: 20, + width: 20, + top: 9, + position: "absolute", + overflow: "hidden", + }, + textTypo: { + display: "flex", + fontFamily: "Poppins-Medium", + fontWeight: "500", + alignItems: "center", + lineHeight: 20, + position: "absolute", + }, + headerTitle: { + top: "0%", + left: "3.02%", + fontSize: 16, + fontWeight: "600", + fontFamily: "Poppins-SemiBold", + color: "#32004c", + textAlign: "left", + lineHeight: 20, + position: "absolute", + }, + header1Child: { + height: "3.45%", + top: "96.55%", + right: "0%", + bottom: "0%", + left: "0%", + maxWidth: "100%", + maxHeight: "100%", + position: "absolute", + }, + header1: { + top: 20, + left: 15, + height: 29, + width: 364, + position: "absolute", + }, + transactionItemFrameChild: { + top: 5, + width: 28, + height: 28, + left: 0, + position: "absolute", + }, + mdishoppingOutlineIcon: { + left: 4, + }, + electronics: { + top: 0, + width: 78, + color: "#000", + left: 40, + display: "flex", + fontFamily: "Poppins-Medium", + fontWeight: "500", + textAlign: "left", + fontSize: 14, + }, + text: { + top: 18, + fontSize: 12, + width: 49, + color: "#000", + left: 40, + display: "flex", + fontFamily: "Poppins-Medium", + fontWeight: "500", + textAlign: "left", + }, + text1: { + left: 304, + color: "#ff0000", + textAlign: "right", + width: 45, + display: "flex", + fontFamily: "Poppins-Medium", + fontWeight: "500", + fontSize: 14, + top: 9, + }, + ioncardOutlineIcon: { + left: 231, + }, + transactionItemFrame: { + height: 38, + width: 364, + }, + transactionItem: { + top: 49, + }, + transactionItem1: { + top: 99, + }, + transactionItem2: { + top: 149, + }, + transactionItem3: { + top: 199, + }, + transactionSection: { + backgroundColor: "#fafafb", + shadowColor: "rgba(0, 0, 0, 0.06)", + shadowOffset: { + width: 0, + height: 26.746257781982422, + }, + shadowRadius: 42.79, + elevation: 42.79, + shadowOpacity: 1, + flex: 1, + height: 276, + }, +}); + +export default RecentTransaction; diff --git a/frontend/app/accounts/VisaCard.js b/frontend/app/accounts/VisaCard.js new file mode 100644 index 0000000..fa3e9c9 --- /dev/null +++ b/frontend/app/accounts/VisaCard.js @@ -0,0 +1,15 @@ +import { StyleSheet, Text, View } from "react-native"; +import React from "react"; +import IconVisa from "../../assets/icons/visa.svg"; + +const VisaCard = () => { + return ( + + + + ); +}; + +export default VisaCard; + +const styles = StyleSheet.create({}); diff --git a/frontend/app/accounts/[id].js b/frontend/app/accounts/[id].js index c2338ab..0776095 100644 --- a/frontend/app/accounts/[id].js +++ b/frontend/app/accounts/[id].js @@ -1,4 +1,4 @@ -import { StyleSheet, Text, View } from "react-native"; +import { StyleSheet, View } from "react-native"; import React from "react"; import { useLocalSearchParams } from "expo-router"; import LinkedAccount from "../../components/LinkedAccount"; @@ -6,6 +6,9 @@ import AccountDetails from "./AccountDetails"; import IconCoins from "../../assets/icons/coins.svg"; import IconTravel from "../../assets/icons/travel.svg"; import IconWallet from "../../assets/icons/wallet.svg"; +import { Text } from "react-native-paper"; +import VisaCard from "./VisaCard"; +import RecentTransaction from "./RecentTransactions"; const AccountPage = ({}) => { const { id, name, type, limit, currency, balance } = useLocalSearchParams(); @@ -13,19 +16,13 @@ const AccountPage = ({}) => { return ( - - Name: {name} - Balance: ${balance} - {currency} - {type} - {limit} + Your recent transactions - - + diff --git a/frontend/assets/icons/transaction-item.svg b/frontend/assets/icons/transaction-item.svg new file mode 100644 index 0000000..4ac31bf --- /dev/null +++ b/frontend/assets/icons/transaction-item.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/assets/icons/visa.svg b/frontend/assets/icons/visa.svg new file mode 100644 index 0000000..416c278 --- /dev/null +++ b/frontend/assets/icons/visa.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/components/OpenAiChat.js b/frontend/components/OpenAiChat.js index 20a66d8..6073df7 100644 --- a/frontend/components/OpenAiChat.js +++ b/frontend/components/OpenAiChat.js @@ -23,17 +23,15 @@ const OpenAiChat = () => { }, []); return ( - + - Powered by OpenAI {aiResponse} - - - {/* Powered by OpenAI */} - - + Powered by OpenAI + + + ); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 573e744..1ec824e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -27,8 +27,10 @@ "react": "18.2.0", "react-hook-form": "^7.51.0", "react-native": "0.73.4", + "react-native-element-dropdown": "^2.10.4", "react-native-gesture-handler": "~2.14.0", "react-native-paper": "^5.12.3", + "react-native-picker-select": "^9.0.1", "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", "react-native-vector-icons": "^10.0.3", @@ -6423,6 +6425,16 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@react-native-picker/picker": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.7.0.tgz", + "integrity": "sha512-QWxfYPMyaCxWwJ9VoiWX4enQ0OZA7Q0O4f5n86b03LqnVyTNVYBnt4dYDTROfDUT9NhlS7Vqy0B3OV4pfyyG1g==", + "peer": true, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.73.1", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.73.1.tgz", @@ -14831,6 +14843,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -19777,6 +19794,21 @@ "react": "18.2.0" } }, + "node_modules/react-native-element-dropdown": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/react-native-element-dropdown/-/react-native-element-dropdown-2.10.4.tgz", + "integrity": "sha512-sbzByNSQmS3iZuNxzeqs0L04MrYQt8uSNUcmnAZAN/vi5IqZ7N0TPHzZ996PSttpYrF2NkiByd+Zlw9Em+VgAw==", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.14.1.tgz", @@ -19818,6 +19850,17 @@ "color-string": "^1.6.0" } }, + "node_modules/react-native-picker-select": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-native-picker-select/-/react-native-picker-select-9.0.1.tgz", + "integrity": "sha512-iIhb82OSH1vqB/HoipvbuX3RCzmeaQmIASlaRbv6X8imNjzh1DH/LaKoF4+DAZJLT0iX7QG79vpwZu8wjWap3Q==", + "dependencies": { + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "@react-native-picker/picker": "^2.4.0" + } + }, "node_modules/react-native-safe-area-context": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.8.2.tgz", @@ -26716,6 +26759,13 @@ "joi": "^17.2.1" } }, + "@react-native-picker/picker": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.7.0.tgz", + "integrity": "sha512-QWxfYPMyaCxWwJ9VoiWX4enQ0OZA7Q0O4f5n86b03LqnVyTNVYBnt4dYDTROfDUT9NhlS7Vqy0B3OV4pfyyG1g==", + "peer": true, + "requires": {} + }, "@react-native/assets-registry": { "version": "0.73.1", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.73.1.tgz", @@ -32854,6 +32904,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -36314,6 +36369,14 @@ } } }, + "react-native-element-dropdown": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/react-native-element-dropdown/-/react-native-element-dropdown-2.10.4.tgz", + "integrity": "sha512-sbzByNSQmS3iZuNxzeqs0L04MrYQt8uSNUcmnAZAN/vi5IqZ7N0TPHzZ996PSttpYrF2NkiByd+Zlw9Em+VgAw==", + "requires": { + "lodash": "^4.17.21" + } + }, "react-native-gesture-handler": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.14.1.tgz", @@ -36347,6 +36410,14 @@ } } }, + "react-native-picker-select": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-native-picker-select/-/react-native-picker-select-9.0.1.tgz", + "integrity": "sha512-iIhb82OSH1vqB/HoipvbuX3RCzmeaQmIASlaRbv6X8imNjzh1DH/LaKoF4+DAZJLT0iX7QG79vpwZu8wjWap3Q==", + "requires": { + "lodash.isequal": "^4.5.0" + } + }, "react-native-safe-area-context": { "version": "4.8.2", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.8.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index fcfba58..f72a788 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,8 +28,10 @@ "react": "18.2.0", "react-hook-form": "^7.51.0", "react-native": "0.73.4", + "react-native-element-dropdown": "^2.10.4", "react-native-gesture-handler": "~2.14.0", "react-native-paper": "^5.12.3", + "react-native-picker-select": "^9.0.1", "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", "react-native-vector-icons": "^10.0.3", From 07a401aed3529245e498c379ac180eb81ea8ff8a Mon Sep 17 00:00:00 2001 From: Alberto Sierra Date: Tue, 26 Mar 2024 02:56:10 -0700 Subject: [PATCH 5/6] styling for account and transactions --- frontend/app/(tabs)/transactions.js | 77 +++++++++++++-------- frontend/app/accounts/RecentTransactions.js | 12 ++-- frontend/app/transactions/[id].js | 22 ++++-- 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/frontend/app/(tabs)/transactions.js b/frontend/app/(tabs)/transactions.js index fac6d86..55d6921 100644 --- a/frontend/app/(tabs)/transactions.js +++ b/frontend/app/(tabs)/transactions.js @@ -1,21 +1,23 @@ import React, { useState, useEffect } from "react"; import { View, - FlatList, ActivityIndicator, StyleSheet, TouchableOpacity, + Text, + FlatList, + Pressable, // Import Text for displaying non-Paragraph elements if needed } from "react-native"; -import { Card, Paragraph } from "react-native-paper"; +import { Button, Card, Paragraph } from "react-native-paper"; import ReceiptScanner from "../../components/ReceiptScanner"; -import { Link } from "expo-router"; +import { Link, router } from "expo-router"; export default function Tab() { const [transactions, setTransactions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isCameraActive, setIsCameraActive] = useState(false); - // Mock data that mimics the structure you expect from your backend + // Mock data const mockTransactions = [ { id: 1, date: "2024-03-14", amount: "50.00", category: "Groceries" }, { id: 2, date: "2024-03-15", amount: "150.00", category: "Utilities" }, @@ -26,20 +28,53 @@ export default function Tab() { ]; useEffect(() => { - // Simulate fetching data by setting the mock data after a delay + // Simulate fetching data const timer = setTimeout(() => { setTransactions(mockTransactions); setIsLoading(false); - }, 1000); // 1 second delay to simulate loading + }, 1000); - // Cleanup the timer when the component unmounts return () => clearTimeout(timer); }, []); const handleCameraActiveChange = (isActive) => { - setIsCameraActive(isActive); // Update based on camera's active state + setIsCameraActive(isActive); }; + const renderTransaction = ({ item }) => ( + + + + + Date: {item.date} + Amount: ${item.amount} + Category: {item.category} + + + + router.push({ + pathname: `/transactions/${item.id}`, + params: { + id: mockTransactions[(0, 1, 2, 3, 4, 5)].id, + amount: mockTransactions[(0, 1, 2, 3, 4, 5)].amount, + date: mockTransactions[(0, 1, 2, 3, 4, 5)].date, + category: mockTransactions[(0, 1, 2, 3, 4, 5)].category, + }, + }) + } + > + + + + + ); + return ( @@ -47,26 +82,12 @@ export default function Tab() { (isLoading ? ( ) : ( - - - - - Date: - Amount: - Category: - - - - - - - Date: - Amount: - Category: - - - - + item.id.toString()} + contentContainerStyle={styles.flatListContent} + /> ))} ); diff --git a/frontend/app/accounts/RecentTransactions.js b/frontend/app/accounts/RecentTransactions.js index 90538be..df5032d 100644 --- a/frontend/app/accounts/RecentTransactions.js +++ b/frontend/app/accounts/RecentTransactions.js @@ -31,7 +31,7 @@ const RecentTransaction = () => { Travel 16-11-23 - -5000 + -$50 { resizeMode="cover" source="mdi:shopping-outline.png" /> - {/* */} + Shopping 16-11-23 - -5000 + -$50 { resizeMode="cover" source="mdi:shopping-outline.png" /> - + Electronics 16-11-23 - -5000 + -$50 { Electronics 16-11-23 - -5000 + -$50 { - const { id } = useLocalSearchParams(); + const { id, mockTransactions, date, amount, category } = + useLocalSearchParams(); + const transactions = JSON.parse(mockTransactions || "[]"); + + console.log(transactions[0]); return ( - - TransactionsPage - {id} + + + Date: {date} + Amount: ${amount} + Category: {category} + ); }; export default TransactionsPage; -const styles = StyleSheet.create({}); +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, +}); From ecf2733f6f758e081157bf67ffc3ef088a01bddc Mon Sep 17 00:00:00 2001 From: Alberto Sierra Date: Tue, 26 Mar 2024 03:08:58 -0700 Subject: [PATCH 6/6] style update --- frontend/components/ReceiptScanner.js | 78 +++++++++++++++++++-------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/frontend/components/ReceiptScanner.js b/frontend/components/ReceiptScanner.js index bca764d..7ad0f05 100644 --- a/frontend/components/ReceiptScanner.js +++ b/frontend/components/ReceiptScanner.js @@ -1,6 +1,14 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { Modal, Button, View, Image, Text, StyleSheet, TouchableOpacity } from 'react-native'; -import { Camera } from 'expo-camera'; +import React, { useState, useEffect, useRef } from "react"; +import { + Modal, + Button, + View, + Image, + Text, + StyleSheet, + TouchableOpacity, +} from "react-native"; +import { Camera } from "expo-camera"; const ReceiptScanner = ({ onCameraActiveChange }) => { const cameraRef = useRef(null); @@ -11,7 +19,7 @@ const ReceiptScanner = ({ onCameraActiveChange }) => { useEffect(() => { (async () => { const { status } = await Camera.requestCameraPermissionsAsync(); - setHasPermission(status === 'granted'); + setHasPermission(status === "granted"); })(); }, []); @@ -29,7 +37,7 @@ const ReceiptScanner = ({ onCameraActiveChange }) => { }; const handleAcceptPicture = () => { - console.log('Picture accepted:', pictureUri); + console.log("Picture accepted:", pictureUri); setIsCameraActive(false); onCameraActiveChange && onCameraActiveChange(false); setPictureUri(null); @@ -50,34 +58,58 @@ const ReceiptScanner = ({ onCameraActiveChange }) => { } if (hasPermission === false) { - return No access to camera; + return ( + + No access to camera + + ); } return ( - + Scan Receipt + onRequestClose={handleCancelCamera} + > {pictureUri ? ( <> - +