From 04c1f050f55a797a3f24c2529da5e7d65fce397d Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 17 Apr 2024 18:10:00 -0400 Subject: [PATCH] copy page, copy services in frontend --- backend/src/routes/user.go | 16 -- frontend/components/ProfilePerformance.tsx | 15 +- frontend/pages/Copy/CopyTradesPage.tsx | 184 +++++++++++++++++++++ frontend/router/BottomNavBar.tsx | 17 +- frontend/services/copy.ts | 10 ++ frontend/services/users.ts | 9 +- frontend/types/navigationTypes.ts | 9 + 7 files changed, 235 insertions(+), 25 deletions(-) create mode 100644 frontend/pages/Copy/CopyTradesPage.tsx create mode 100644 frontend/services/copy.ts diff --git a/backend/src/routes/user.go b/backend/src/routes/user.go index c9e35939..671dc494 100644 --- a/backend/src/routes/user.go +++ b/backend/src/routes/user.go @@ -15,22 +15,6 @@ func SetupUserRoutes(router *gin.Engine, db *gorm.DB, clerkClient clerk.Client) userRoutes := router.Group("/users") { - /* Protected Routes */ - - /* - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Clerk middleware currently doesn't work since ngrok does not allow us to - edit request headers. - - We can: - - implement our own clerk middleware - - find out how to modify request headers with axios and bypass ngrok - - find out how to modify request headers with ngrok options dynamically - - add middleware to filter all requests and attach headers once they reach the backend (might conflict with clerk) - - */ - // SetupAuthMiddleware(clerkClient, router) - // Routes that only read/write DB userRoutes.GET("", userController.GetAllUsers) userRoutes.POST("", userController.CreateUser) diff --git a/frontend/components/ProfilePerformance.tsx b/frontend/components/ProfilePerformance.tsx index 5189ec6a..a909f65f 100644 --- a/frontend/components/ProfilePerformance.tsx +++ b/frontend/components/ProfilePerformance.tsx @@ -1,11 +1,20 @@ import { View, Text, Pressable } from 'react-native' import React from 'react' +import { useNavigation } from '@react-navigation/native'; +import { ProfileNavigationProp } from '../types/navigationTypes'; +import { useSession } from '@clerk/clerk-expo'; interface ProfilePerformanceProps { portfolioValue: number } const ProfilePerformance = ({portfolioValue} : ProfilePerformanceProps) => { + const { session } = useSession(); + const navigator = useNavigation(); + const handleCopyTrades = () => { + navigator.navigate('CopyTrades') + } + return ( Performance @@ -13,11 +22,11 @@ const ProfilePerformance = ({portfolioValue} : ProfilePerformanceProps) => { = 0 ? 'text-[#02AD98]' : 'text-[#FF2B51]'}`}> {portfolioValue} % - - Copy Trades - YTD Performance + {(session?.user.username == 'nathan') ? + Copy Trades + : null} ) } diff --git a/frontend/pages/Copy/CopyTradesPage.tsx b/frontend/pages/Copy/CopyTradesPage.tsx new file mode 100644 index 00000000..7e7f13c5 --- /dev/null +++ b/frontend/pages/Copy/CopyTradesPage.tsx @@ -0,0 +1,184 @@ +import React, { useEffect, useState } from 'react'; +import { + View, + Text, + TextInput, + Switch, + TouchableOpacity, + StyleSheet, + Alert, +} from 'react-native'; +import { useNavigation } from '@react-navigation/native'; +import { useSession } from '@clerk/clerk-expo'; +import { + ProfileNavigationProp, + AuthNavigationProp, +} from '../../types/navigationTypes'; +import { User } from '../../types/types'; +import { getUserById } from '../../services/users'; +import { copyTrades } from '../../services/copy'; + +function CopyTradesPage() { + const { session } = useSession(); + const navigation = useNavigation(); + const authNavigation = useNavigation(); + const [investmentAmount, setInvestmentAmount] = useState('1000'); + const [stopLossEnabled, setStopLossEnabled] = useState(false); + const [stopLossAmount, setStopLossAmount] = useState('500'); + const [tradeAuthorizationRequired, setTradeAuthorizationRequired] = + useState(false); + const [user, setUser] = useState(null); + + const handleSubmit = async () => { + if (!user) { + Alert.alert('Error', "User doesn't exist"); + return; + } + if (!session?.user.id) { + authNavigation.navigate('Login'); + return; + } + + try { + await copyTrades(session?.user.id as string, user.username); + } catch (error) { + Alert.alert('Error', 'Failed to copy trades'); + return; + } + + Alert.alert('Success', 'Trades copied successfully'); + navigation.navigate('Profile'); + }; + + useEffect(() => { + const fetchUser = async () => { + try { + const user = await getUserById(''); + setUser(user); + } catch (error) { + console.error('Error fetching user:', error); + } + }; + + fetchUser(); + }, []); + + return ( + + + @kevinkevindaliri + navigation.navigate('Profile')}> + × + + + + ACCT 123456 + {/* */} + + + Investment Amount + + + This investment will proportionally copy this investor’s portfolio. + + + + Set stop loss + + + {stopLossEnabled && ( + + Investment Falls Below + + + )} + + Require trade authorization + + + + Copy Trades + + + ); +} + +const styles = StyleSheet.create({ + container: { + padding: 16, + backgroundColor: '#FFF', + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 24, + }, + username: { + fontWeight: 'bold', + }, + closeButton: { + fontSize: 24, + }, + accountInfo: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: 12, + backgroundColor: '#F5F5F5', + borderRadius: 8, + marginBottom: 16, + }, + accountLabel: { + fontWeight: 'bold', + }, + // potentially add arrowIcon styling if using an Image component + inputGroup: { + marginBottom: 16, + }, + label: { + marginBottom: 8, + }, + input: { + borderWidth: 1, + borderColor: '#E0E0E0', + padding: 12, + borderRadius: 8, + marginBottom: 8, + }, + smallText: { + color: '#757575', + fontSize: 12, + }, + switchContainer: { + marginBottom: 24, + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + }, + submitButton: { + backgroundColor: '#4CAF50', + borderRadius: 8, + padding: 16, + alignItems: 'center', + }, + submitButtonText: { + color: 'white', + fontWeight: 'bold', + }, +}); + +export default CopyTradesPage; diff --git a/frontend/router/BottomNavBar.tsx b/frontend/router/BottomNavBar.tsx index d5358b5a..b3c9c61a 100644 --- a/frontend/router/BottomNavBar.tsx +++ b/frontend/router/BottomNavBar.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; -//import AuthPage from '../pages/AuthPage'; import { Icon } from '@rneui/themed'; import { RouteProp } from '@react-navigation/native'; import Profile from '../pages/Profile'; import FeedPage from '../pages/FeedPage'; import Leaderboard from '../pages/Leaderboard'; -// import AuthNavigator from './AuthNavigation'; -// import { useSession } from '@clerk/clerk-expo'; +import CopyTradesPage from '../pages/Copy/CopyTradesPage'; +import { createStackNavigator } from '@react-navigation/stack'; + const Tab = createBottomTabNavigator(); type TabRouteName = @@ -63,7 +63,7 @@ const BottomNavBar = () => { /> { ); }; + +const ProfileStack = createStackNavigator(); +const ProfileStackNavigator = () => ( + + + + +); + export default BottomNavBar; \ No newline at end of file diff --git a/frontend/services/copy.ts b/frontend/services/copy.ts new file mode 100644 index 00000000..7131449f --- /dev/null +++ b/frontend/services/copy.ts @@ -0,0 +1,10 @@ +import axios, { AxiosResponse } from 'axios'; +import { API_LINK } from './CommonDocs'; +import { Redirect } from '../types/types'; + +export const copyTrades = async (currentUserId: string, targetUserId: string) => { + const response: AxiosResponse = await axios.post( + `http://${API_LINK}/portfolio/${currentUserId}/${targetUserId}` + ); + return response.data; +}; diff --git a/frontend/services/users.ts b/frontend/services/users.ts index 174a5c34..94a72767 100644 --- a/frontend/services/users.ts +++ b/frontend/services/users.ts @@ -7,7 +7,6 @@ export const getAllUsers = async (): Promise => { const response: AxiosResponse = await axios.get( `http://${API_LINK}/users`, ); - // console.log(response.data); return response.data; }; @@ -24,6 +23,12 @@ export const registerUser = async ( LongTermGoals: longTermGoals, }, ); - // console.log(JSON.stringify(response)); return response.data; }; + +export const getUserById = async (id: string): Promise => { + const response: AxiosResponse = await axios.get( + `http://${API_LINK}/users/${id}`, + ); + return response.data; +}; \ No newline at end of file diff --git a/frontend/types/navigationTypes.ts b/frontend/types/navigationTypes.ts index 60acf660..aaead0ff 100644 --- a/frontend/types/navigationTypes.ts +++ b/frontend/types/navigationTypes.ts @@ -21,6 +21,11 @@ export type RootStackParamList = { // MainApp: undefined; }; +export type ProfileStackParamList = { + Profile: undefined; + CopyTrades: undefined; +}; + export type AuthNavigationProp = StackNavigationProp< RootStackParamList >; @@ -30,6 +35,10 @@ export type LevelPageNavigationProp = StackNavigationProp< 'LevelPage' >; +export type ProfileNavigationProp = StackNavigationProp< + ProfileStackParamList +>; + // export type ConnectPageNavigationProp = StackNavigationProp< // RootStackParamList, // 'ConnectPage'