Skip to content

Commit

Permalink
copy page, copy services in frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
capture120 committed Apr 17, 2024
1 parent 5c692b7 commit 04c1f05
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 25 deletions.
16 changes: 0 additions & 16 deletions backend/src/routes/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
15 changes: 12 additions & 3 deletions frontend/components/ProfilePerformance.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
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<ProfileNavigationProp>();
const handleCopyTrades = () => {
navigator.navigate('CopyTrades')
}

return (
<View className='flex flex-col justify-between space-y-1 py-3 px-6 border-b-[1px] border-b-gray-200'>
<Text className='font-semibold text-base'>Performance</Text>
<View className='flex flex-row justify-between items-center'>
<Text className={`text-2xl ${portfolioValue >= 0 ? 'text-[#02AD98]' : 'text-[#FF2B51]'}`}>
{portfolioValue} %
</Text>
<Pressable>
<Text>Copy Trades</Text>
</Pressable>
</View>
<Text>YTD Performance</Text>
{(session?.user.username == 'nathan') ? <Pressable onPress={handleCopyTrades}>
<Text>Copy Trades</Text>
</Pressable> : null}
</View>
)
}
Expand Down
184 changes: 184 additions & 0 deletions frontend/pages/Copy/CopyTradesPage.tsx
Original file line number Diff line number Diff line change
@@ -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<ProfileNavigationProp>();
const authNavigation = useNavigation<AuthNavigationProp>();
const [investmentAmount, setInvestmentAmount] = useState('1000');
const [stopLossEnabled, setStopLossEnabled] = useState(false);
const [stopLossAmount, setStopLossAmount] = useState('500');
const [tradeAuthorizationRequired, setTradeAuthorizationRequired] =
useState(false);
const [user, setUser] = useState<User | null>(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 (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.username}>@kevinkevindaliri</Text>
<TouchableOpacity onPress={() => navigation.navigate('Profile')}>
<Text style={styles.closeButton}>×</Text>
</TouchableOpacity>
</View>
<View style={styles.accountInfo}>
<Text style={styles.accountLabel}>ACCT 123456</Text>
{/* <Image source={require('.png')} style={styles.arrowIcon} /> */}
</View>
<View style={styles.inputGroup}>
<Text style={styles.label}>Investment Amount</Text>
<TextInput
style={styles.input}
value={investmentAmount}
onChangeText={setInvestmentAmount}
keyboardType="numeric"
/>
<Text style={styles.smallText}>
This investment will proportionally copy this investor’s portfolio.
</Text>
</View>
<View style={styles.switchContainer}>
<Text style={styles.label}>Set stop loss</Text>
<Switch onValueChange={setStopLossEnabled} value={stopLossEnabled} />
</View>
{stopLossEnabled && (
<View style={styles.input}>
<Text style={styles.label}>Investment Falls Below</Text>
<TextInput
value={stopLossAmount}
onChangeText={setStopLossAmount}
style={styles.input}
keyboardType="numeric"
/>
</View>
)}
<View style={styles.switchContainer}>
<Text style={styles.label}>Require trade authorization</Text>
<Switch
onValueChange={setTradeAuthorizationRequired}
value={tradeAuthorizationRequired}
/>
</View>
<TouchableOpacity style={styles.submitButton} onPress={handleSubmit}>
<Text style={styles.submitButtonText}>Copy Trades</Text>
</TouchableOpacity>
</View>
);
}

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;
17 changes: 13 additions & 4 deletions frontend/router/BottomNavBar.tsx
Original file line number Diff line number Diff line change
@@ -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<BottomTabParamList>();
type TabRouteName =
Expand Down Expand Up @@ -63,7 +63,7 @@ const BottomNavBar = () => {
/>
<Tab.Screen
name="Profile"
component={Profile}
component={ProfileStackNavigator}
options={{
headerShown: false,
}}
Expand All @@ -72,4 +72,13 @@ const BottomNavBar = () => {
);
};


const ProfileStack = createStackNavigator();
const ProfileStackNavigator = () => (
<ProfileStack.Navigator initialRouteName="Profile">
<ProfileStack.Screen name="Profile" component={Profile} />
<ProfileStack.Screen name="CopyTrades" component={CopyTradesPage} />
</ProfileStack.Navigator>
);

export default BottomNavBar;
10 changes: 10 additions & 0 deletions frontend/services/copy.ts
Original file line number Diff line number Diff line change
@@ -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<Redirect> = await axios.post<Redirect>(
`http://${API_LINK}/portfolio/${currentUserId}/${targetUserId}`
);
return response.data;
};
9 changes: 7 additions & 2 deletions frontend/services/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const getAllUsers = async (): Promise<User[]> => {
const response: AxiosResponse<User[]> = await axios.get<User[]>(
`http://${API_LINK}/users`,
);
// console.log(response.data);
return response.data;
};

Expand All @@ -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<User> => {
const response: AxiosResponse<User> = await axios.get<User>(
`http://${API_LINK}/users/${id}`,
);
return response.data;
};
9 changes: 9 additions & 0 deletions frontend/types/navigationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export type RootStackParamList = {
// MainApp: undefined;
};

export type ProfileStackParamList = {
Profile: undefined;
CopyTrades: undefined;
};

export type AuthNavigationProp = StackNavigationProp<
RootStackParamList
>;
Expand All @@ -30,6 +35,10 @@ export type LevelPageNavigationProp = StackNavigationProp<
'LevelPage'
>;

export type ProfileNavigationProp = StackNavigationProp<
ProfileStackParamList
>;

// export type ConnectPageNavigationProp = StackNavigationProp<
// RootStackParamList,
// 'ConnectPage'
Expand Down

0 comments on commit 04c1f05

Please sign in to comment.