Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scrum 21 portfolio screen 2 #71

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
edd37d2
Adding in the card component
abby-stevenson Nov 11, 2024
2be6453
Merge branch 'main' into SCRUM-21-portfolio-screen-2
abby-stevenson Nov 11, 2024
70341d6
Supabase changes
abby-stevenson Nov 11, 2024
2b93952
Merge branch 'main' into SCRUM-21-portfolio-screen-2
abby-stevenson Nov 11, 2024
b318bfc
Fonts and more styling
abby-stevenson Nov 12, 2024
e5e88cc
Added tasks to do
abby-stevenson Nov 12, 2024
652415e
Merge branch 'main' into SCRUM-21-portfolio-screen-2
abby-stevenson Nov 14, 2024
cec8c4e
Merge branch 'main' into SCRUM-21-portfolio-screen-2
abby-stevenson Nov 17, 2024
66ca47b
Adding the tag component
abby-stevenson Nov 18, 2024
d1b4c6c
Text Colour changes
abby-stevenson Nov 21, 2024
b6cd72f
Merge branch 'main' into SCRUM-21-portfolio-screen-2
Nov 24, 2024
2f78237
Merged Main and starting to connect to backend
abby-stevenson Nov 26, 2024
1497d92
Connecting the bottom half of the screen to the backend
abby-stevenson Nov 30, 2024
f5d4715
Changes to Portfolio Details Component
abby-stevenson Nov 30, 2024
07acaea
Adding changing colours for negative values
abby-stevenson Nov 30, 2024
bd0de14
Values on the portfolio can be hidden
abby-stevenson Dec 2, 2024
1ed30fd
Picking the tags
abby-stevenson Dec 3, 2024
0415781
Removing mock data
abby-stevenson Dec 3, 2024
4116689
Temporary Change to file names
abby-stevenson Dec 3, 2024
96232e6
Temporary File Name Changes
abby-stevenson Dec 3, 2024
364dcbc
Merge branch 'main' into SCRUM-21-portfolio-screen-2
abby-stevenson Dec 3, 2024
79b8743
Undoing of the Temporary File Name Changes
abby-stevenson Dec 3, 2024
cfb88d4
Removing Hard Coded Values
abby-stevenson Dec 3, 2024
660ca46
Removing Unused Components
abby-stevenson Dec 3, 2024
1dbd932
Removing unnecessary comments
abby-stevenson Dec 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions frontend/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ function RootNavigator() {
return session ? <TabNavigator /> : <LoginNavigator />;
}

function TestNavigator() {
const { login } = useAuth();
login("[email protected]", "password123")
if (isLoading) {
return null; // or some loading screen (maybe we make in future?)
}
return <TabNavigator/>
}


export default function App() {
const [loaded, error] = useFonts({
'Nunito-Black': require('./assets/fonts/nunito/Nunito-Black.ttf'),
Expand Down
Binary file added frontend/assets/images/Tab.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/images/Tab1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/images/Tab2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/assets/images/View-Icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"react-test-renderer": "18.1.0",
"ts-jest": "^29.2.4",
"typescript": "~5.3.3",
"uuid": "^10.0.0"
"uuid": "^10.0.0",
"wonka": "^6.3.4"
},
"devDependencies": {
"@babel/core": "^7.25.2",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export function Tag({
<StyledView className={`${tagVariants({ level })} ${className || ''}`} {...props}>
<StyledView className={`${tagIconVariants({ level })}`}>{icon}</StyledView>
<StyledText className={`${tagTextVariants({ level })} ${icon ? 'ml-1' : ''}`}>

{children}
</StyledText>
</StyledView>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const API_URL = "https://44c7-155-33-132-43.ngrok-free.app";
export const SUPABASE_URL = "https://145e-155-33-132-43.ngrok-free.app";
export const API_URL = "https://cbdc-155-33-135-54.ngrok-free.app";
export const SUPABASE_URL = "https://d9c1-155-33-135-54.ngrok-free.app";
export const SUPABASE_JWT_SECRET = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0";

297 changes: 174 additions & 123 deletions frontend/src/screens/portfolio/PortfolioScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React, { useState } from 'react';
//Change loading page and if a portfolio cant be loaded page
import React, { useEffect, useState } from 'react';
import { Image, Text, View, TouchableOpacity } from 'react-native';
import { NavigationScreenProp } from 'react-navigation';
import { styled } from 'nativewind';
import { ScrollView } from 'react-native';
import PortfolioItem from './components/PortfolioItem';
import PortfolioDetails from './components/PortfolioDetails';
import UpdateCard from './components/PortfolioUpdateCard';
import { useProjectTotalFunded, useAllProjects, useProjectPosts } from "../../services/project";
import {useInvestorHistory, useInvestorPortfolio} from "../../services/investor";



interface PortfolioScreenProps {
// This actually should be `any`, so disabling the linter rule
Expand All @@ -17,11 +22,106 @@ const StyledView = styled(View);
const StyledText = styled(Text);
const StyledScrollView = styled(ScrollView);
const StyledImage = styled(Image);
const StyledTouchableOpacity = styled(TouchableOpacity);
const StyledTouchableOpacity= styled(TouchableOpacity);

function calculateDaysRemaining(completionDateStr) {
const today = new Date();
const [monthStr, yearStr] = completionDateStr.split(" ");
const completionDate = new Date(`${monthStr} 1, ${yearStr}`);

const timeDifference = completionDate.getTime() - today.getTime();

return Math.ceil(timeDifference / (1000 * 60 * 60 * 24));
}

function calculateExpMaturity(projects) {
let totalDays = 0;
let validProjects = 0;

projects.forEach(project => {
const daysRemaining = calculateDaysRemaining(project.completion_date);

if (daysRemaining !== null) {
totalDays += daysRemaining;
validProjects++;
}
});

if (validProjects === 0) {
console.error("No valid projects to calculate average maturity.");
return null;
}

return totalDays / validProjects;
}

function netPortfolioValue(projects) {
let value = 0;

projects.forEach(project => {
value = value + project.funding_goal_cents
});

return value / 100;
}

function calculateMarketValue(investments) {
let marketValue = 0;

investments.forEach(investment => {
marketValue = marketValue + investment.fundedCents
});

return marketValue/100;
}

const [totalInvested, setTotalInvested] = useState(0);
const { portfolio } = useInvestorPortfolio();

useEffect(() => {
if (portfolio) {
const total = Object.values(portfolio).reduce((sum, value) => sum + value, 0);
setTotalInvested(total);
}
}, [portfolio]);


export default function PorfolioScreen({ navigation }: PortfolioScreenProps) {
const [activeTab, setActiveTab] = useState('Your Projects');

const { portfolio, isLoading: portfolioLoading } = useInvestorPortfolio();
const { allProjects, isLoading: projectsLoading } = useAllProjects();
const {history, isLoading: historyLoading } = useInvestorHistory(1, 1000); //dont know what to put for the pages - maybe change later?

console.log(portfolio);

if (portfolioLoading || projectsLoading || historyLoading) {
return (
<StyledView className='flex-1 justify-center bg-surfaceBG overflow-auto'>
<StyledView className='flex w-93 h-150 items-center shrink-0'></StyledView>
<StyledText className = 'text-error justify-center'>Loading</StyledText>
</StyledView>
);
}

if (!portfolio) {
return (
<StyledView className='flex-1 justify-center bg-surfaceBG overflow-auto'>
<StyledView className='flex w-93 h-150 items-center shrink-0'></StyledView>
<StyledText className = 'text-error justify-center'>Failed to load portfolio. Please try again later.</StyledText>
</StyledView>
)
}

if (!allProjects || allProjects.length === 0) {
return (
<StyledView className='flex-1 justify-center bg-surfaceBG overflow-auto'>
<StyledView className='flex w-93 h-14 items-center shrink-0'></StyledView>
<StyledText className = 'text-error justify-center'>No projects.</StyledText>
</StyledView>
)
}

return (
<StyledView className='flex-1 justify-center bg-surfaceBG overflow-auto'>
{/* Padding */}
Expand All @@ -30,139 +130,90 @@ export default function PorfolioScreen({ navigation }: PortfolioScreenProps) {
<StyledScrollView contentContainerStyle={{ flexGrow: 1 }} className='flex-1'>
<StyledView className='flex justify-between items-left self-stretch px-6 py-3'>
<StyledView className='flex flex-row justify-between'>
<StyledText>Your Portfolio</StyledText>
<StyledText className = 'font-heading text-[24px]'>Your Portfolio</StyledText>
<StyledView className='items-right justify-right'>
{/* <StyledImage
source={require('../../assets/images/remove_red_eye.png')}
<StyledImage
source={require('../../../assets/images/settings.png')}
className='w-[6vw] h-[6vw]'
></StyledImage> */}
></StyledImage>
</StyledView>
</StyledView>
</StyledView>

<PortfolioDetails
netPortfolioValue={netPortfolioValue(allProjects)}
portfolioChangeAmount={350} // HARD CODED VALUE
marketValue={calculateMarketValue(history)}
cashValue={totalInvested}
totalProjects={allProjects.length}
expMaturity={calculateExpMaturity(allProjects)}>

<PortfolioDetails></PortfolioDetails>
</PortfolioDetails>

{/* Tab section */}
<StyledView className='flex-row justify-center'>
<StyledView className=' py-2.5 px-5 bg-white cursor-pointer transition-colors duration-300 hover:bg-gray-100 active:bg-green-900 active:text-white'>
<StyledTouchableOpacity
onPress={() => setActiveTab('Your Projects')}
className={`items-center py-2 rounded-t-lg ${activeTab === 'Your Projects' ? 'bg-green-900' : 'bg-white'}`}
>
<StyledText
className={`text-lg font-bold ${activeTab === 'Your Projects' ? 'text-white' : 'text-gray-800'}`}
>
Your Projects
</StyledText>
</StyledTouchableOpacity>
</StyledView>
<StyledView className=' py-2.5 px-5 bg-white cursor-pointer transition-colors duration-300 hover:bg-gray-100 active:bg-green-900 active:text-white'>
<StyledTouchableOpacity
onPress={() => setActiveTab('Updates')}
className={` items-center py-2 rounded-t-lg ${activeTab === 'Updates' ? 'bg-green-900' : 'bg-white'}`}
>
<StyledText
className={`text-lg font-bold ${activeTab === 'Updates' ? 'text-white' : 'text-gray-800'}`}
>
Updates
</StyledText>
</StyledTouchableOpacity>
</StyledView>
</StyledView>

<StyledView className='p-5 bg-gray-50 border border-gray-300 rounded-b-lg'>
{/*Your Projects*/}
{activeTab === 'Your Projects' ? (
<StyledView className='flex w-98 p-[16px_24px] flex-col items-start gap-3 rounded-[27px] bg-white'>
<StyledText className='font-sourceSans3CaptionMedium text-defaultText'>
{' '}
8 Total Investments
</StyledText>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<PortfolioItem
address={''}
location={''}
price={0}
duration={''}
invested={0}
completion={0}
imageUrl={''}
></PortfolioItem>
<StyledView className='w-full flex-row justify-center'>
<StyledView className = 'w-40'>
<StyledTouchableOpacity onPress={() => setActiveTab('Your Projects')} className={`items-center h-14 py-2 rounded-tr-[27px] rounded-tl-[27px] ${activeTab === 'Your Projects' ? 'bg-white' : 'bg-defaultPrimary'}`}>
<StyledText className={`text-lg font-bold ${activeTab === 'Your Projects' ? 'text-gray-800' : 'text-white'}`}>
Your Projects
</StyledText>
</StyledTouchableOpacity>
</StyledView>
) : (
<StyledView className='flex w-98 p-[16px_24px] flex-col items-start gap-3 rounded-[27px] bg-white'>
{/* Updates */}
<UpdateCard
topText='931 1st Street'
bottomText='You invested $200'
quantity='+$200.00'
{/* {activeTab === 'Updates' ? (
<StyledImage className = 'h-14 w-10 z=1'
source={require('../../../assets/images/Tab2.png')}
/>
) : <StyledImage className = 'h-14 z=1'
source={require('../../../assets/images/Tab1.png')}
/>} */}
<StyledView className = 'w-40'>
<StyledTouchableOpacity onPress={() => setActiveTab('Updates')} className={`items-center h-14 py-2 rounded-tr-[27px] ${activeTab === 'Updates' ? 'bg-white' : 'bg-defaultPrimary'}`}>
<StyledText className={`text-lg font-bold ${ activeTab === 'Updates' ? 'text-gray-800' : 'text-white'}`}>
Updates
</StyledText>
</StyledTouchableOpacity>
</StyledView>
)}
</StyledView>


{/*Your Projects*/}
{activeTab === 'Your Projects' ? (
<StyledView className = 'bg-defaultPrimary'>
<StyledView className= {`flex p-[16px] flex-col items-start bg-white ${activeTab === 'Your Projects' ? 'rounded-tr-[27px]' : 'rounded-tl-[27px]'}`}>
<StyledText className='font-sourceSans3CaptionMedium text-[14px] mb-4'>
{allProjects.length} Total Investments
</StyledText>
{allProjects.map((project) => (
<StyledView className = 'mb-4'>
<PortfolioItem
address={project.street}
location={project.state}
image={<Image key={project.images[0].id} src={project.images[0].url} alt="Project Image" />}
status={project.milestone}
description={project.description}
initialValue={useProjectTotalFunded(project.id).projectTotalFunded}
finalValue={project.funding_goal_cents/ 100}></PortfolioItem>
</StyledView>))}
</StyledView>
</StyledView>

)
:
(<StyledView className = 'bg-defaultPrimary'>
<StyledView className={`flex p-[16px] flex-col items-start bg-white ${activeTab === 'Updates' ? 'rounded-tl-[27px]' : 'rounded-tr-[27px]'}`}>
{/* Updates */}
{allProjects.flatMap(project => {
const { projectPosts = [] } = useProjectPosts(project.id) ?? {};
return projectPosts.map((post) => (
<StyledView key={post.id} className="mb-2">
<UpdateCard topText={post.title} bottomText={post.description} status={project.milestone} />
</StyledView>
));
})}
</StyledView>
</StyledView>
)}
</StyledScrollView>
</StyledView>
);
}
};
Loading
Loading