aboutsummaryrefslogtreecommitdiff
path: root/react-frontend
diff options
context:
space:
mode:
Diffstat (limited to 'react-frontend')
-rw-r--r--react-frontend/public/assets/outline_cancel_white_18dp.pngbin0 -> 388 bytes
-rw-r--r--react-frontend/public/assets/outline_close_white_18dp.pngbin0 -> 181 bytes
-rw-r--r--react-frontend/public/assets/outline_minimize_white_18dp.pngbin0 -> 97 bytes
-rw-r--r--react-frontend/public/assets/outline_search_white_18dp.pngbin0 -> 397 bytes
-rw-r--r--react-frontend/public/assets/outline_tune_white_18dp.pngbin0 -> 171 bytes
-rw-r--r--react-frontend/public/assets/round_arrow_back_white_18dp.png (renamed from react-frontend/public/round_arrow_back_white_18dp.png)bin163 -> 163 bytes
-rw-r--r--react-frontend/public/assets/round_expand_less_white_18dp.pngbin0 -> 170 bytes
-rw-r--r--react-frontend/public/assets/round_expand_more_white_18dp.pngbin0 -> 166 bytes
-rw-r--r--react-frontend/public/link-icon.svg1
-rw-r--r--react-frontend/src/App.js23
-rw-r--r--react-frontend/src/components/EdgeInfo.js59
-rw-r--r--react-frontend/src/components/Hub.js17
-rw-r--r--react-frontend/src/components/HubList.js89
-rw-r--r--react-frontend/src/components/HubWidget.js84
-rw-r--r--react-frontend/src/components/InvestorInfo.js181
-rw-r--r--react-frontend/src/components/TimeSelector.js3
-rw-r--r--react-frontend/src/components/Visualization.js81
-rw-r--r--react-frontend/src/components/WatchDogs.js21
-rw-r--r--react-frontend/src/components/images/person.svg1
-rw-r--r--react-frontend/src/css/App.css1
-rw-r--r--react-frontend/src/css/CoordSelector.css4
-rw-r--r--react-frontend/src/css/EdgeInfo.css0
-rw-r--r--react-frontend/src/css/InvesterInfo.css62
-rw-r--r--react-frontend/src/css/UserCheckin.css39
24 files changed, 533 insertions, 133 deletions
diff --git a/react-frontend/public/assets/outline_cancel_white_18dp.png b/react-frontend/public/assets/outline_cancel_white_18dp.png
new file mode 100644
index 0000000..b989e20
--- /dev/null
+++ b/react-frontend/public/assets/outline_cancel_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/assets/outline_close_white_18dp.png b/react-frontend/public/assets/outline_close_white_18dp.png
new file mode 100644
index 0000000..65867f3
--- /dev/null
+++ b/react-frontend/public/assets/outline_close_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/assets/outline_minimize_white_18dp.png b/react-frontend/public/assets/outline_minimize_white_18dp.png
new file mode 100644
index 0000000..a6a12d6
--- /dev/null
+++ b/react-frontend/public/assets/outline_minimize_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/assets/outline_search_white_18dp.png b/react-frontend/public/assets/outline_search_white_18dp.png
new file mode 100644
index 0000000..52ffe5f
--- /dev/null
+++ b/react-frontend/public/assets/outline_search_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/assets/outline_tune_white_18dp.png b/react-frontend/public/assets/outline_tune_white_18dp.png
new file mode 100644
index 0000000..4168a63
--- /dev/null
+++ b/react-frontend/public/assets/outline_tune_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/round_arrow_back_white_18dp.png b/react-frontend/public/assets/round_arrow_back_white_18dp.png
index bbaccda..bbaccda 100644
--- a/react-frontend/public/round_arrow_back_white_18dp.png
+++ b/react-frontend/public/assets/round_arrow_back_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/assets/round_expand_less_white_18dp.png b/react-frontend/public/assets/round_expand_less_white_18dp.png
new file mode 100644
index 0000000..a64f430
--- /dev/null
+++ b/react-frontend/public/assets/round_expand_less_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/assets/round_expand_more_white_18dp.png b/react-frontend/public/assets/round_expand_more_white_18dp.png
new file mode 100644
index 0000000..f8c7213
--- /dev/null
+++ b/react-frontend/public/assets/round_expand_more_white_18dp.png
Binary files differ
diff --git a/react-frontend/public/link-icon.svg b/react-frontend/public/link-icon.svg
new file mode 100644
index 0000000..926cbcb
--- /dev/null
+++ b/react-frontend/public/link-icon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg fill="none" height="24" stroke="lightgreen" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" x2="21" y1="14" y2="3"/></svg> \ No newline at end of file
diff --git a/react-frontend/src/App.js b/react-frontend/src/App.js
index 2cef235..1461c5a 100644
--- a/react-frontend/src/App.js
+++ b/react-frontend/src/App.js
@@ -12,18 +12,17 @@ import mainlogo from './images/mainlogo.png';
import './css/Landing.css';
function App() {
- const [startApp, setStartApp] = useState(false);
-
- const startModal = () => {
- document.getElementById("main-modal").style.display = 'block';
- setStartApp(false);
- }
- const exitModal = () => {
- document.getElementById("main-modal").style.display = 'none';
- setStartApp(true);
- }
-
-
+ const [startApp, setStartApp] = useState(false);
+
+ const startModal = () => {
+ document.getElementById("main-modal").style.display = 'block';
+ setStartApp(false);
+ }
+ const exitModal = () => {
+ document.getElementById("main-modal").style.display = 'none';
+ setStartApp(true);
+ }
+
return (
<>
diff --git a/react-frontend/src/components/EdgeInfo.js b/react-frontend/src/components/EdgeInfo.js
new file mode 100644
index 0000000..566c70a
--- /dev/null
+++ b/react-frontend/src/components/EdgeInfo.js
@@ -0,0 +1,59 @@
+
+import '../css/UserCheckin.css';
+import { useEffect, useState } from "react";
+
+/**
+ * Componenet for checkins. Has a toggle to show more info.
+ * @param {Object} props The props of the component.
+ * @returns {import('react').HtmlHTMLAttributes} A list element holding a checkin's info.
+ */
+function EdgeInfo(props) {
+ /* // State - toggled
+ const [stockList, setStockList] = useState([]);
+
+ const stockInfo = stockList.map((stock) =>
+ <li>{stock}</li>
+ );
+
+
+ const getEdgeInfo = (fromID, toID) => {
+ fetch("http://localhost:4567/edge-data", {
+ method: "POST",
+ body: JSON.stringify({
+ followerID: fromID,
+ leaderID: toID,
+ }),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin"
+ })
+ .then(res => res.json())
+ .then(data => {
+ console.log(data);
+ setStockList(data);
+ })
+ .catch(err => console.log(err));
+ }
+
+ useEffect(() => getEdgeInfo(), [props.selectedFollowerID, props.selectedLeaderID]);
+ */
+
+
+ return (
+ <div>
+
+ </div>
+ /*<div className='Chosen-user' hidden={!props.showEdgeInfo}>
+ <h3>
+ <span onClick={() => props.setShowEdgeInfo(false)}>
+ <img className="Img-btn" src="assets/round_arrow_back_white_18dp.png" alt="image" />
+ </span>
+ </h3>
+ <div className = 'edge-info'>
+ {stockInfo}
+ </div>
+ </div>*/);
+}
+
+export default EdgeInfo; \ No newline at end of file
diff --git a/react-frontend/src/components/Hub.js b/react-frontend/src/components/Hub.js
index 8a3ac1c..1906684 100644
--- a/react-frontend/src/components/Hub.js
+++ b/react-frontend/src/components/Hub.js
@@ -10,12 +10,25 @@ import '../css/UserCheckin.css';
* @returns {import('react').HtmlHTMLAttributes} A list element holding a checkin's info.
*/
function Hub(props) {
- // State - toggled
+ const LEN_NAME = 15;
+
+ const [isHover, setIsHover] = useState(false);
+
+ const formatName = name => {
+ if (name.length >= LEN_NAME) {
+ return props.name.substring(0, LEN_NAME - 3) + '...';
+ }
+ return props.name;
+ }
return (
<li className='Checkin'>
<div className="Img-flex">
- <span className="Clickable-name" onClick= {() => console.log(props.id)}>{props.name}</span>
+ <span
+ className="Clickable-name"
+ onMouseOver = {() => setIsHover(true)}
+ onMouseLeave = {() => setIsHover(false)}
+ onClick = {() => props.setSelectedId(props.id)}>{isHover || props.searching ? props.name : formatName(props.name)}</span>
<span>{props.value.toFixed(3)}</span>
</div>
</li>);
diff --git a/react-frontend/src/components/HubList.js b/react-frontend/src/components/HubList.js
index 0df3020..383d570 100644
--- a/react-frontend/src/components/HubList.js
+++ b/react-frontend/src/components/HubList.js
@@ -1,63 +1,64 @@
// React and component imports
import { useEffect, useState } from "react";
import Hub from "./Hub.js";
-import InvestorInfo from "./InvestorInfo.js";
+import uuid from 'react-uuid';
// CSS import
-import '../css/UserCheckin.css';
+import "../css/UserCheckin.css";
/**
* Component that build the checkin list and displays checkin info.
- * @returns {import('react').HtmlHTMLAttributes} A div with the hubs
+ * @returns {import('react').HtmlHTMLAttributes} A div with the hubs
* in a vertical layout.
*/
function HubList(props) {
- const [hubItems, setHubItems] = useState([]);
- const [isSelected, setIsSelected] = useState(false);
- const [name, setName] = useState('');
-
+ const [displayedItems, setDisplayedItems] = useState([]);
+
/**
- * Loads new the checkins into the current cache/map of hubs.
+ * Method that determines whehter the Hub should be showed.
+ * @returns {Boolean} True if to be shown, false if not.
*/
- const updateHubItems = () => {
- // sort and create the elemnts
- let hubs = [];
- //const sorted = props.data.sort((a, b) => b.suspicionScore - a.suspicionScore);
- props.data.forEach(hub => hubs.push(
- <Hub key={hub.id} id={hub.id} name={hub.name} value={hub.suspicionScore} setSelected={props.setSelected}></Hub>
- ));
-
- setHubItems(hubs);
+ const toInclude = holder => {
+ // TODO: add number search or differentiate between it
+ // TODO: add sus score range....
+ if (!holder) {
+ return false;
+ };
+
+ // const matchingId = holder.id.toString().includes(queryString.toLowerCase());
+ //console.log(props.queryString.toLowerCase(), props.data.length);
+ const matchingName = holder.name.toLowerCase().includes(props.queryString.toLowerCase());
+ return matchingName;
}
- const getName = () => {
- props.data.forEach(hub => {
- if (hub.id == props.selected) {
- setName(hub.name);
- }
- })
- setName('');
+ const toHubElement = hub =>
+ <Hub
+ id={hub.id}
+ name={hub.name}
+ value={hub.suspicionScore}
+ setSelectedId={props.setSelectedId}
+ searching={props.searching}
+ ></Hub>;
+
+ /**
+ * Filters the items to be shown, then created the iteams and sets the state with the items.
+ */
+ const filterItems = () => {
+ if (!props.queryString) {
+ // don't need to show all unless searching
+ return setDisplayedItems(props.data.slice(0,600).map(hub => toHubElement(hub)))
+ }
+
+ const criteria = props.data.filter(holder => toInclude(holder));
+ setDisplayedItems(criteria.map(hub => toHubElement(hub)));
}
-
- // React hook that updates when the hubs are recalculated
- useEffect(() => updateHubItems(), [props.data]);
-
- //React hook to show data for an investor
- useEffect(() => {
- setIsSelected(true)
- getName();
- }, [props.selected]);
-
- return (
- <div className="User-checkin">
- <div className="Checkins">
- <h2>Suspicion Ranks</h2>
- <ul className='Checkin-list'>{hubItems}</ul>
- </div>
- <InvestorInfo personId={props.selected} isSelected={isSelected} name={name} dates={props.dates}></InvestorInfo>
- </div>
- );
+ /**
+ * Hook to update the items on change of the search string or update of data.
+ */
+ useEffect(() => filterItems(), [props.queryString, props.data, props.searching]);
+
+ return <ul className='Checkin-list'>{displayedItems}</ul>;
}
-export default HubList; \ No newline at end of file
+export default HubList;
diff --git a/react-frontend/src/components/HubWidget.js b/react-frontend/src/components/HubWidget.js
new file mode 100644
index 0000000..f0434d7
--- /dev/null
+++ b/react-frontend/src/components/HubWidget.js
@@ -0,0 +1,84 @@
+// React and component imports
+import { useEffect, useState, useRef } from "react";
+import Hub from "./Hub.js";
+import InvestorInfo from "./InvestorInfo.js";
+
+// CSS import
+import '../css/UserCheckin.css';
+import HubList from "./HubList.js";
+
+/**
+ * Component that build the checkin list and displays checkin info.
+ * @returns {import('react').HtmlHTMLAttributes} A div with the hubs
+ * in a vertical layout.
+ */
+function HubWidget(props) {
+ // States for selected person
+ const [followers, setFollowers] = useState([]);
+ const [selectedName, setSelectedName] = useState([]);
+
+ const [searching, setSearching] = useState(false);
+ const textInput = useRef();
+
+ const [showSelectedInfo, setShowSelectedInfo] = useState(false);
+
+ const updateSelected = () => {
+ props.data.forEach(holder => {
+ if (holder.id === props.selectedId) {
+ console.log(holder);
+ setFollowers(holder.followers);
+ setSelectedName(holder.name);
+ }
+ });
+ }
+
+ // Don't need to fetch if repeat id...
+ const updateSelectedId = id => {
+ setShowSelectedInfo(true);
+ props.setSelectedId(id);
+ }
+
+ // Hook to update and show the info when a user is clicked on ...
+ useEffect(() => {
+ updateSelected();
+ }, [props.selectedId]);
+ // On init, don't show...
+ useEffect(() => setShowSelectedInfo(false), []);
+
+ useEffect(() => {
+ if (searching) {
+ return;
+ }
+ textInput.current.value = "";
+ setQueryString("");
+ }, [searching])
+
+ const [queryString, setQueryString] = useState("");
+
+ return (
+ <>
+ <div className="User-checkin">
+ <div className="Checkins">
+ <h2 className="Img-flex Title">
+ <span hidden={searching}>Sus Ranks</span>
+ <input ref={textInput} hidden={!searching} type="search" onChange={e => setQueryString(e.target.value)} placeholder="Search by name"></input>
+ <img className="Img-btn" hidden={searching} onClick={() => setSearching(true)} src="assets/outline_search_white_18dp.png" alt="image"/>
+ <img className="Img-btn" hidden={!searching} onClick={() => setSearching(false)} src="assets/outline_cancel_white_18dp.png" alt="image"/>
+ </h2>
+ <HubList data={props.data} setSelectedId={updateSelectedId} queryString={queryString} searching={searching}></HubList>
+ </div>
+ <InvestorInfo
+ selectedId={props.selectedId}
+ setSelectedId={updateSelectedId}
+ dates={props.dates}
+ setShowSelectedInfo={setShowSelectedInfo}
+ showSelectedInfo={showSelectedInfo}
+ followers={followers}
+ name={selectedName}
+ ></InvestorInfo>
+ </div>
+ </>
+ );
+}
+
+export default HubWidget; \ No newline at end of file
diff --git a/react-frontend/src/components/InvestorInfo.js b/react-frontend/src/components/InvestorInfo.js
index d368984..9e18299 100644
--- a/react-frontend/src/components/InvestorInfo.js
+++ b/react-frontend/src/components/InvestorInfo.js
@@ -2,7 +2,9 @@
import { useEffect, useState } from "react";
// CSS import
-import '../css/UserCheckin.css';
+import "../css/UserCheckin.css";
+import "../css/InvesterInfo.css";
+import uuid from "react-uuid";
/**
* Componenet for checkins. Has a toggle to show more info.
@@ -10,57 +12,154 @@ import '../css/UserCheckin.css';
* @returns {import('react').HtmlHTMLAttributes} A list element holding a checkin's info.
*/
function InvestorInfo(props) {
- const [info, setInfo] = useState({});
+ const [info, setInfo] = useState({
+ percentGain: 0,
+ moneyIn: 0,
+ moneyOut: 0,
+ percentSP500: 0,
+ holdings: [],
+ });
- const toEpochMilli = date => Date.parse(date);
- const getInfo = () => {
- console.log({
- person: props.name,
- start: toEpochMilli(props.dates.start),
- end: toEpochMilli(props.dates.end)
- });
+ const [showStocks, setShowStocks] = useState(false);
+ const [showFollowers, setShowFollowers] = useState(false);
- if (props.name === "") {
+ const getInfo = () => {
+ if (props.selectedId === -1) {
return;
}
-
+
+ const toEpochMilli = (date) => Date.parse(date);
+
+ console.log({
+ selectedId: props.selectedId,
+ startTime: toEpochMilli(props.dates.start),
+ endTime: toEpochMilli(props.dates.end),
+ });
+
fetch("http://localhost:4567/profit", {
- method: "POST",
- body: JSON.stringify({
- person: props.name,
- start: toEpochMilli(props.dates.start),
- end: toEpochMilli(props.dates.end)
- }),
- headers: {
- "Content-Type": "application/json",
- },
- credentials: "same-origin"
+ method: "POST",
+ body: JSON.stringify({
+ selectedId: props.selectedId,
+ startTime: toEpochMilli(props.dates.start),
+ endTime: toEpochMilli(props.dates.end),
+ }),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ credentials: "same-origin",
})
- .then(res => {
- console.log(res);
- res.json();
- })
- .then(data => {
- console.log(data);
- setInfo(data);
- })
- .catch(err => console.log(err));
- }
- /*
+ .then(res => res.json())
+ .then(data => {
+ console.log(data);
+ setInfo(data);
+ props.setShowSelectedInfo(true);
+ })
+ .catch(err => console.log(err));
+ };
+
+ const stockTable = () => {
+ return (
+ <>
+ <li class="Stock-row">
+ <div className="tableHeader">
+ <div className="symbol-row">Symbol</div>
+ <div className="gain-row">Realized gain</div>
+ <div className="gain-row">Unrealized gain</div>
+ </div>
+ </li>
+ {info.holdings.map((holding) => (
+ <li class="Stock-row">
+ <div className="symbol-row">{holding.ticker}</div>
+ <div className="gain-row">{holding.realizedGain.toFixed(3)}</div>
+ <div className="gain-row">{holding.unrealizedGain.toFixed(3)}</div>
+ </li>
+ ))}
+ </>
+ );
+ };
- const coords = userCoords.map((coord, index) =>
- <li key={index}>
- <span>{'('+coord[0].toFixed(6)}, {coord[1].toFixed(6)+')'}</span>
- </li>
- );*/
+ const followerList = () =>
+ props.followers.map((follower) => (
+ <li key={uuid()} class="Clickable-name" onClick={() => props.setSelectedId(follower.id)}>
+ {follower.name}
+ </li>
+ ));
- useEffect(() => getInfo(), [props.name, props.isSelected, props.personId])
+ // Hook that updates when selected has changed
+ useEffect(() => getInfo(), [props.selectedId]);
return (
- <div className="Chosen-user" hidden={props.isSelected}>
- hi
+ <div className="Chosen-user" hidden={!props.showSelectedInfo}>
+ <h3>
+ <span onClick={() => props.setShowSelectedInfo(false)}>
+ <img className="Img-btn" src="assets/round_arrow_back_white_18dp.png" alt="image" />
+ </span>
+ <span>CIK: {props.selectedId}</span>
+ </h3>
+ <h2 id="investerName">
+ <a href={"https://sec.report/CIK/" + props.selectedId + "/Insider-Trades"} target="_blank">
+ {props.name}
+ <img src="link-icon.svg" alt="image" />
+ </a>
+ </h2>
+ <div id="top-bar">
+ <div id="gain-number">
+ <p className="bigNumber">${(info.moneyOut - info.moneyIn).toFixed(3)}</p> gained
+ </div>
+
+ <div>
+ <p className="bigNumber">{info.percentGain.toFixed(3)}%</p>
+ compared to {info.percentSP500.toFixed(3)}% on SP500
+ </div>
+ </div>
+
+ <div>
+ <div className="Checkin" hidden={info.holdings.length === 0}>
+ <div className="Img-flex">
+ <span className="tableHeader">View holdings</span>
+ <img
+ className="Img-btn"
+ hidden={showStocks}
+ onClick={() => setShowStocks((toggle) => !toggle)}
+ src="assets/round_expand_more_white_18dp.png"
+ alt="image"
+ />
+ <img
+ className="Img-btn"
+ hidden={!showStocks}
+ onClick={() => setShowStocks((toggle) => !toggle)}
+ src="assets/round_expand_less_white_18dp.png"
+ alt="image"
+ />
+ </div>
+ <ul hidden={!showStocks} class="Stock-table">
+ {stockTable()}
+ </ul>
+ </div>
+
+ <div className="Checkin" hidden={!followerList}>
+ <div className="Img-flex">
+ <span className="tableHeader">View followers</span>
+ <img
+ className="Img-btn"
+ hidden={showFollowers}
+ onClick={() => setShowFollowers((toggle) => !toggle)}
+ src="assets/round_expand_more_white_18dp.png"
+ alt="image"
+ />
+ <img
+ className="Img-btn"
+ hidden={!showFollowers}
+ onClick={() => setShowFollowers((toggle) => !toggle)}
+ src="assets/round_expand_less_white_18dp.png"
+ alt="image"
+ />
+ </div>
+ <ul hidden={!showFollowers}>{followerList()}</ul>
+ </div>
+ </div>
</div>
);
}
-export default InvestorInfo; \ No newline at end of file
+export default InvestorInfo;
diff --git a/react-frontend/src/components/TimeSelector.js b/react-frontend/src/components/TimeSelector.js
index 652a9ec..997494d 100644
--- a/react-frontend/src/components/TimeSelector.js
+++ b/react-frontend/src/components/TimeSelector.js
@@ -34,9 +34,8 @@ function TimeSelector(props) {
<DateSelector side={"left"} name={"Start Date"} className="Coord-select-left" clickedFunc={setCurrent}
changedFunc={setStartDate} disabled={current==='start' || props.isChanging} value={toValue(startDate)}></DateSelector>
<div>
- <h2>Adjust Timeframe</h2>
<button className="Btn Route-btn" onClick={() => changeTimeframe()}
- disabled={current!=="" || props.isChanging}>Change Timeframe</button>
+ disabled={current!=="" || props.isChanging}>Adjust Timeframe</button>
</div>
<DateSelector side={"right"} name={"End Date"} className="Coord-select-right" clickedFunc={setCurrent}
changedFunc={setEndDate} disabled={current==='end' || props.isChanging} value={toValue(endDate)}></DateSelector>
diff --git a/react-frontend/src/components/Visualization.js b/react-frontend/src/components/Visualization.js
index 1975e86..d65d106 100644
--- a/react-frontend/src/components/Visualization.js
+++ b/react-frontend/src/components/Visualization.js
@@ -1,63 +1,114 @@
// JS module imports
-import { useEffect, useRef, useState } from "react";
-import uuid from 'react-uuid';
+import { useMemo, useState } from "react";
import Graph from 'vis-react';
// CSS imports
import '../css/Canvas.css';
+import EdgeInfo from "./EdgeInfo";
+
/**
- * This function renders and mantains thhe canvas.
+ * This function renders and mantains the canvas.
* @param {Object} props The props for the canvas.
* @returns {import("react").HtmlHTMLAttributes} The canvas to be retured.
*/
function Visualization(props) {
+
+
+
+ const [key, setKey] = useState(0);
+
+ const [graphState, setGraphState] = useState({
+ nodes: [],
+ edges: []
+ });
+
const options = {
+ autoResize: true,
edges: {
color: "#ffffff"
}
};
+
const events = {
- select: () => event => props.setSelected(event.nodes[0])
- };
+ selectNode: event => {
+ props.setSelectedId(event.nodes[0])
+ }
+ }
- const [graphState, setGraphState] = useState({
- nodes: [],
- edges: []
- });
const getNodes = () => {
let nodes = [];
+ const maxScore = props.data[0].suspicionScore;
+ const interval = maxScore / 4;
+
props.data.forEach(hub => {
if (hub.followers) {
+ let colorVal = '#f6f7d4';
+ const score = hub.suspicionScore;
+
+ if(score > (maxScore - interval)){
+ colorVal = '#d92027'
+ }
+ if(score <= (maxScore - interval) && score > (maxScore - interval*2)){
+ colorVal = '#f37121'
+ }
+ if(score <= (maxScore - interval*2) && score > (maxScore - interval*3)){
+ colorVal = '#fdca40'
+ }
nodes.push({
id: hub.id,
+ autoResize: true,
label: hub.name,
- size: hub.suspicionScore
+ labelHighlightBold: true,
+ shape: "dot",
+ value: hub.suspicionScore*1000,
+ color: {
+ background: colorVal,
+ border: '#2b2e4a',
+ highlight:{
+ background: '#29bb89',
+ border: '#fdca40'
+ }
+ },
+ font: {
+ color: '#9fd8df',
+ size: 20,
+ }
});
}
});
return nodes;
}
+
const getEdges = () => {
let edges = []
props.data.forEach(hub => {
hub.followers.forEach(follower => {
edges.push({
- from: hub.id,
- to: follower.id
+ from: follower.id,
+ to: hub.id,
+ dashes: false,
+ color:{
+ opacity: 0.7,
+ highlight:'#fdca40',
+ },
});
});
});
return edges;
}
- // Hooks to update graph state
- useEffect(() => setGraphState({nodes: getNodes(), edges: getEdges()}), [JSON.stringify(props.data)]);
+ // Hooks to update graph state when data changes
+ useMemo(() => {
+ setKey(key => key + 1);
+ setGraphState({nodes: getNodes(), edges: getEdges()})
+ }, [JSON.stringify(props.data)]);
+
return (
<div className="Map-canvas">
<Graph
- key={uuid()}
+ key={key}
graph={graphState}
options={options}
events={events}>
diff --git a/react-frontend/src/components/WatchDogs.js b/react-frontend/src/components/WatchDogs.js
index d631ea9..f7a9e74 100644
--- a/react-frontend/src/components/WatchDogs.js
+++ b/react-frontend/src/components/WatchDogs.js
@@ -2,7 +2,7 @@
import React, {useEffect, useState} from 'react';
import TimeSelector from './TimeSelector.js';
import Visualization from './Visualization.js';
-import HubList from './HubList.js';
+import HubWidget from './HubWidget.js';
import Loading from './Loading.js';
import Modal from './Modal.js';
import logo from './images/logo.png';
@@ -26,8 +26,8 @@ function WatchDogs() {
});
// State for visualization data
const [data, setData] = useState([]);
- // State for selected person
- const [selected, setSelected] = useState(-1);
+ // State for selectedId
+ const [selectedId, setSelectedId] = useState(-1);
const toEpochMilli = date => Date.parse(date);
const getGraphData = () => {
@@ -49,9 +49,14 @@ function WatchDogs() {
.then(res => res.json())
.then(data => {
//TODO: optimize this
- const sliced = data.holders.slice(0, 500);
- console.log(sliced);
- setData(sliced);
+ //const sliced = data.holders.slice(0, 500);
+ //console.log(sliced);
+ console.log(data);
+ if(data.holders.length === 0) {
+ alert("There is no data between those timeframes :(");
+ return;
+ }
+ setData(data.holders);
setHasLoaded(true);
})
.catch(err => console.log(err));
@@ -77,9 +82,9 @@ function WatchDogs() {
<div className="Canvas-filler Canvas-filler-1"></div>
<div className="Canvas-filler Canvas-filler-2"></div>
<div className="Canvas-filler Canvas-filler-3"></div>
- <HubList setHasLoaded={setHasLoaded} data={data} setSelected={setSelected} selected={selected} dates={dates}></HubList>
+ <HubWidget setHasLoaded={setHasLoaded} data={data} dates={dates} selectedId={selectedId} setSelectedId={setSelectedId}></HubWidget>
<TimeSelector isChanging={isChanging} dates={dates} setDates={setDates}></TimeSelector>
- <Visualization hasLoaded={hasLoaded} data={data} setSelected={setSelected}></Visualization>
+ <Visualization hasLoaded={hasLoaded} data={data.slice(0, 600)} setSelectedId={setSelectedId}></Visualization>
</div>
}
</>
diff --git a/react-frontend/src/components/images/person.svg b/react-frontend/src/components/images/person.svg
new file mode 100644
index 0000000..6a93d8f
--- /dev/null
+++ b/react-frontend/src/components/images/person.svg
@@ -0,0 +1 @@
+<svg xmlns='http://www.w3.org/2000/svg' class='ionicon' viewBox='0 0 512 512'><title>Person</title><path d='M332.64 64.58C313.18 43.57 286 32 256 32c-30.16 0-57.43 11.5-76.8 32.38-19.58 21.11-29.12 49.8-26.88 80.78C156.76 206.28 203.27 256 256 256s99.16-49.71 103.67-110.82c2.27-30.7-7.33-59.33-27.03-80.6zM432 480H80a31 31 0 01-24.2-11.13c-6.5-7.77-9.12-18.38-7.18-29.11C57.06 392.94 83.4 353.61 124.8 326c36.78-24.51 83.37-38 131.2-38s94.42 13.5 131.2 38c41.4 27.6 67.74 66.93 76.18 113.75 1.94 10.73-.68 21.34-7.18 29.11A31 31 0 01432 480z'/></svg> \ No newline at end of file
diff --git a/react-frontend/src/css/App.css b/react-frontend/src/css/App.css
index e39eb3e..1b8de5f 100644
--- a/react-frontend/src/css/App.css
+++ b/react-frontend/src/css/App.css
@@ -7,6 +7,7 @@
grid-template-columns: max-content auto max-content max-content;
background-color: #121212;
+
}
.App-logo {
diff --git a/react-frontend/src/css/CoordSelector.css b/react-frontend/src/css/CoordSelector.css
index 881be08..e1fde99 100644
--- a/react-frontend/src/css/CoordSelector.css
+++ b/react-frontend/src/css/CoordSelector.css
@@ -49,4 +49,8 @@
width: 90%;
}
+.Flex-coord {
+ margin: auto;
+}
+
diff --git a/react-frontend/src/css/EdgeInfo.css b/react-frontend/src/css/EdgeInfo.css
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/react-frontend/src/css/EdgeInfo.css
diff --git a/react-frontend/src/css/InvesterInfo.css b/react-frontend/src/css/InvesterInfo.css
new file mode 100644
index 0000000..c1bb137
--- /dev/null
+++ b/react-frontend/src/css/InvesterInfo.css
@@ -0,0 +1,62 @@
+#top-bar {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-evenly;
+ margin-right: 10px;
+ margin-left: 10px;
+ border-bottom: solid 1px white;
+ margin-top: 0px;
+ padding-bottom: 20px;
+}
+
+/* div {
+ border: solid white;
+} */
+
+/* p {
+ border: solid white;
+} */
+
+.bigNumber {
+ font-size: 16pt;
+ margin: 0;
+}
+
+.Chosen-user {
+ background-color: #333333;
+ color: lightgreen;
+ width: 25vw;
+ border-radius: 10px;
+ overflow: scroll;
+}
+
+.Stock-row {
+ display: flex;
+ justify-content: space-evenly;
+ color: white;
+ margin-bottom: 5px;
+}
+
+.tableHeader {
+ display: flex;
+ flex-direction: row;
+ justify-content: left;
+ margin-right: 10px;
+ margin-left: 10px;
+ font-size: 120%;
+ padding: 5px;
+ color: lightgreen;
+}
+
+.symbol-row {
+ margin-right: 20px;
+}
+
+.gain-row {
+ margin-right: 20px;
+}
+
+a {
+ color: lightgreen;
+ text-decoration: none;
+}
diff --git a/react-frontend/src/css/UserCheckin.css b/react-frontend/src/css/UserCheckin.css
index 141cc01..e60b348 100644
--- a/react-frontend/src/css/UserCheckin.css
+++ b/react-frontend/src/css/UserCheckin.css
@@ -10,6 +10,7 @@
cursor: default;
/* Transparent background */
background: rgba(0, 0, 0, 0);
+ overflow: scroll;
}
ul {
@@ -20,7 +21,7 @@ ul {
z-index: 10;
background-color: #333333;
border-radius: 20px;
- margin: 5px;
+ margin: 5px 5px 5px 0;
}
.Coord-ex {
@@ -30,23 +31,29 @@ ul {
text-align: center;
}
+.Chosen-user > h3, .Checkins > h3 {
+ display: flex;
+ justify-content: space-between;
+ height: 5vh;
+ padding: 0 30px;
+}
+
.Chosen-user > h2, .Checkins > h2 {
display: flex;
- justify-content: space-evenly;
+ justify-content: center;
height: 5vh;
- padding: 0 10px;
+ padding: 0 30px;
}
.Checkin-list {
padding: 0 20px;
- height: 86vh;
- overflow-y: scroll;
- cursor: default;
+ height: 85vh;
+ overflow: scroll;
}
.User-checkin-list {
- height: 80vh;
- overflow-y: scroll;
+ height: 75vh;
+ overflow: scroll;
list-style-position: inside;
padding: 0 20px;
@@ -79,7 +86,7 @@ ul {
.Img-btn {
background-color: #424242;
border-radius: 50%;
- margin-right: 10px;
+ margin-left: auto;
}
.Img-btn:hover {
@@ -91,4 +98,18 @@ ul {
cursor: pointer;
text-decoration: underline;
color: lightgreen;
+}
+
+/* CSS borrowed from W3 Schools */
+input[type=search] {
+ width: 80%;
+ padding: 12px 20px;
+ box-sizing: border-box;
+ font-size: 80%;
+ background-color: lightgoldenrodyellow;
+ border-radius: 2;
+}
+
+.Title {
+ margin-top: 20px;
} \ No newline at end of file