protect endpoints

This commit is contained in:
MuslemRahimi 2024-09-23 01:20:45 +02:00
parent 2827d4dde3
commit aab619dc21
101 changed files with 1004 additions and 5130 deletions

View File

@ -15,7 +15,7 @@ async function createPortfolio(event) {
const postData = {'userId': data?.user?.id}
const response = await fetch(data?.fastifyURL+'/create-portfolio', {
const response = await fetch('/api/create-portfolio', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -1,521 +0,0 @@
<script lang='ts'>
import toast from 'svelte-french-toast';
import {screenWidth, currentPortfolioPrice, displayCompanyName, traded, stockTicker, etfTicker, cryptoTicker, assetType} from '$lib/store';
export let data;
export let availableCash;
export let holdingShares;
let fastifyURL = import.meta.env.VITE_USEAST_FASTIFY_URL;
let estimatedTotal = 0;
let numberOfShares = 0;
let commissionPrice = 0;
let displayTab = 'changeOrder';
function handleMaxOrder()
{
if ($currentPortfolioPrice > availableCash)
{
toast.error(`Not enough money to buy ${$stockTicker} shares`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
else
{
numberOfShares = Math.floor(availableCash/$currentPortfolioPrice);
estimatedTotal = (numberOfShares * $currentPortfolioPrice);
}
}
function handleInputChange(event) {
const inputValue = event.target.value;
if (!isNaN(inputValue) && Number(inputValue) >= 0) {
numberOfShares = Number(inputValue);
} else {
numberOfShares = 0;
}
estimatedTotal = numberOfShares * $currentPortfolioPrice;
}
function handleAppendNumber(num) {
// Append the clicked number to the current numberOfShares
numberOfShares = numberOfShares * 10 + num;
}
function handleRemoveNumber() {
// Remove the last digit from numberOfShares or set to zero if NaN
const numStr = numberOfShares.toString();
numberOfShares = numStr.length > 1 ? parseInt(numStr.slice(0, -1), 10) : 0;
}
function changeTab() {
if(displayTab === 'changeOrder' && numberOfShares > 0 && estimatedTotal <= availableCash)
{
displayTab = 'buyOrder';
}
else if (displayTab === 'buyOrder')
{
displayTab = 'changeOrder';
}
else {
toast.error('Please check your Input values again.', {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
}
async function handleBuyOrder()
{
const postData = {
'userId': data?.user?.id,
'symbol': $assetType === 'stock' ? $stockTicker : $assetType === 'etf' ? $etfTicker : $cryptoTicker,
'assetType': $assetType,
'name': $displayCompanyName,
'numberOfShares': numberOfShares,
'estimatedTotal': estimatedTotal,
'boughtPrice': $currentPortfolioPrice,
}
// Make the POST request to the endpoint
const response = await fetch(fastifyURL+'/buy-stock', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
});
const output = (await response.json())?.items;
if (output === 'success')
{
toast.success(`Successfully bought ${numberOfShares} ${$stockTicker} shares`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
numberOfShares = 0;
estimatedTotal = 0;
displayTab = 'changeOrder';
$traded = true;
const closePopup = document.getElementById("buyTradeModal");
closePopup?.dispatchEvent(new MouseEvent('click'))
}
else if (output === 'failure')
{
toast.error(`Something went wrong. Please try again.`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
else if (output === 'marketClosed')
{
toast.error(`The market is closed. Please try again later.`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
}
$: {
if(numberOfShares)
{
estimatedTotal = (numberOfShares * $currentPortfolioPrice);
}
}
</script>
<svelte:head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
</svelte:head>
{#if $screenWidth >= 640}
<!--Start Trade Modal-->
<input type="checkbox" id="buyTradeModal" class="modal-toggle" />
<dialog id="buyTradeModal" class="modal modal-bottom sm:modal-middle ">
<label for="buyTradeModal" class="cursor-pointer modal-backdrop bg-[#fff] bg-opacity-[0.08]"></label>
<div class="modal-box rounded-none w-full bg-[#000] h-[500px]" >
<!--Start Trade Modal-->
<div class="flex flex-col w-full">
{#if displayTab === 'changeOrder'}
<div class= "flex flex-col">
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-[#09090B] mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $assetType === 'etf' ? $etfTicker : $cryptoTicker}.png`} loading="lazy"/>
</div>
<span class="mb-1">
{$displayCompanyName?.length > 35 ? $displayCompanyName?.slice(0, 35) + "..." : $displayCompanyName}
</span>
<span class="mb-1 text-sm font-medium">
Current Price: ${$currentPortfolioPrice}
</span>
</div>
{#if holdingShares !== 0}
<span class="text-blue-400 text-sm ml-auto font-medium">
Holding Shares: {holdingShares}
</span>
{/if}
</div>
<div class="flex justify-between items-center mt-2">
<div class="flex flex-col sm:w-64">
<label class="label font-medium">
<span class="text-white label-text">Number of Shares</span>
</label>
<input bind:value={numberOfShares} on:input={handleInputChange} type="text" placeholder="Number of Shares" class="text-slate-200 input {estimatedTotal > availableCash ? 'input-error' : ''} bg-gray-900 input-md w-54 sm:w-full max-w-xs" />
</div>
<label on:click={handleMaxOrder} class="mt-9 btn bg-[#000] hover:bg-[#fff] hover:text-black text-white cursor-pointer sm:px-5 border rounded-lg">
Show Max
</label>
</div>
<span class="{estimatedTotal < availableCash ? 'hidden' : ''} label-text-alt text-error mt-1">
Not enough money to buy the amount
</span>
<div class="flex justify-between items-center mt-8 w-full">
<div class="flex flex-col sm:flex-row text-blue-400 font-medium text-sm">
<span class="mr-0 sm:mr-1">
Available Cash:
</span>
<span>
{availableCash?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</span>
</div>
<div class="flex flex-col sm:flex-row text-blue-400 text-sm">
<span class="mr-0 sm:mr-1">
Estimated Total:
</span>
<span>
{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</span>
</div>
</div>
<div class="w-5/6 max-w-lg pt-10 pb-20 m-auto">
<button on:click={changeTab} class="btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Preview Buy Order
</button>
</div>
{:else if displayTab === 'buyOrder'}
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-gray-900 mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $etfTicker}.png`} loading="lazy"/>
</div>
<span class="mb-1 font-medium text-xl">
Buying {numberOfShares} shares of {$displayCompanyName?.length > 35 ? $displayCompanyName?.slice(0, 35) + "..." : $displayCompanyName}
</span>
</div>
<table class="table table-sm table-compact mt-3 text-start flex justify-start items-center w-full px-3 m-auto">
<tbody>
<!-- row 1 -->
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Price per Share</td>
<td class="bg-[#000] whitespace-normal">${$currentPortfolioPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Number of Shares</td>
<td class="bg-[#000] whitespace-normal">{numberOfShares}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Commission</td>
<td class="bg-[#000] whitespace-normal">${commissionPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Estimated Total</td>
<td class="bg-[#000] whitespace-normal">{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Available Cash After</td>
<td class="bg-[#000] whitespace-normal">{(availableCash-estimatedTotal-commissionPrice)?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
</tbody>
</table>
<div class="max-w-lg pt-10 m-auto pb-5 flex flex-row justify-center items-center">
<button on:click={changeTab} class="w-3/4 mr-8 btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md rounded-lg text-white font-bold text-md">
Change Order
</button>
<button on:click={handleBuyOrder} class="w-3/4 btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md rounded-lg text-white font-bold text-md">
Buy
</button>
</div>
{/if}
</div>
<!--End Trade Modal-->
</div>
</dialog>
{:else}
<!--Start Drawer Sidewise for mobile-->
<div class="drawer drawer-end z-40">
<input id="buyTradeModal" type="checkbox" class="drawer-toggle"/>
<div class="drawer-side">
<div class="modal-box overflow-hidden rounded-xl bg-[#000] min-h-screen w-screen" >
<div class="flex flex-col w-full mt-14">
{#if displayTab === 'changeOrder'}
<div class= "flex flex-col">
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-[#09090B] mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $etfTicker}.png`} loading="lazy"/>
</div>
<span class="mb-1">
{$displayCompanyName?.length > 35 ? $displayCompanyName?.slice(0, 35) + "..." : $displayCompanyName}
</span>
<span class="mb-1 text-sm font-medium">
Current Price: ${$currentPortfolioPrice}
</span>
</div>
{#if holdingShares !== 0}
<span class="text-blue-400 text-sm ml-auto font-medium">
Holding Shares: {holdingShares}
</span>
{/if}
</div>
<div class="flex justify-between items-center mt-2">
<div class="flex flex-col sm:w-64">
<label class="label font-medium">
<span class="text-white label-text">Number of Shares</span>
</label>
<input inputmode="tel" pattern="[0-9]*" bind:value={numberOfShares} on:input={handleInputChange} type="text" placeholder="Number of Shares" class="text-slate-200 input {estimatedTotal > availableCash ? 'input-error' : ''} bg-gray-900 input-md w-54 sm:w-full max-w-xs" />
</div>
<label on:click={handleMaxOrder} class="mt-9 btn bg-[#000] hover:bg-[#fff] hover:text-black text-white cursor-pointer sm:px-5 border rounded-lg">
Show Max
</label>
</div>
<span class="{estimatedTotal < availableCash ? 'hidden' : ''} label-text-alt text-error mt-1">
Not enough money to buy the amount
</span>
<div class="flex justify-between items-center mt-8 w-full">
<div class="flex flex-col sm:flex-row text-blue-400 font-medium text-sm">
<span class="mr-0 sm:mr-1">
Available Cash:
</span>
<span>
{availableCash?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</span>
</div>
<div class="flex flex-col sm:flex-row text-blue-400 text-sm">
<span class="mr-0 sm:mr-1">
Estimated Total:
</span>
<span>
{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</span>
</div>
</div>
<!--
<div class="grid grid-cols-3 gap-x-6 gap-y-2 text-white font-bold text-lg m-auto mt-14">
{#each Array.from({ length: 10 }, (_, index) => index+1) as num}
{#if num < 10}
<label on:click={() => handleAppendNumber(num)} class="rounded-full flex justify-center items-center w-16 h-16">
{num}
</label>
{/if}
{/each}
<div class="rounded-full flex justify-center items-center w-16 h-16">
</div>
<div class="rounded-full flex justify-center items-center w-16 h-16 hover:bg-gray-600 hover:bg-opacity-[0.4]">
0
</div>
<label on:click={() => handleRemoveNumber()} class="rounded-full flex justify-center items-center w-16 h-16 hover:bg-gray-600 hover:bg-opacity-[0.4]">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(-90 12 12)"><path fill="white" d="M13 7.828V20h-2V7.828l-5.364 5.364l-1.414-1.414L12 4l7.778 7.778l-1.414 1.414L13 7.828Z"/></g></svg>
</label>
</div>
-->
<div class="w-5/6 max-w-lg pt-16 pb-10 m-auto">
<button on:click={changeTab} class="btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Preview Buy Order
</button>
</div>
{:else if displayTab === 'buyOrder'}
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-gray-900 mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $etfTicker}.png`} loading="lazy"/>
</div>
<span class="mb-1 font-medium text-xl">
Buying {numberOfShares} shares of {$displayCompanyName?.length > 35 ? $displayCompanyName?.slice(0, 35) + "..." : $displayCompanyName}
</span>
</div>
<table class="table table-sm table-compact mt-3 text-start flex justify-start items-center w-full px-3 m-auto">
<tbody>
<!-- row 1 -->
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Price per Share</td>
<td class="bg-[#000] whitespace-normal">${$currentPortfolioPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Number of Shares</td>
<td class="bg-[#000] whitespace-normal">{numberOfShares}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Commission</td>
<td class="bg-[#000] whitespace-normal">${commissionPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Estimated Total</td>
<td class="bg-[#000] whitespace-normal">{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Available Cash After</td>
<td class="bg-[#000] whitespace-normal">{(availableCash-estimatedTotal-commissionPrice)?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
</tbody>
</table>
<div class="w-5/6 max-w-lg pt-10 m-auto pb-5 flex flex-col items-center">
<button on:click={changeTab} class="btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Change Order
</button>
<button on:click={handleBuyOrder} class="mt-6 btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Buy Order
</button>
</div>
{/if}
</div>
<label for="buyTradeModal" class="absolute left-6 top-4 sm:hidden">
<svg class="w-6 h-6 inline-block mb-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#fff" d="M9.125 21.1L.7 12.7q-.15-.15-.213-.325T.425 12q0-.2.063-.375T.7 11.3l8.425-8.425q.35-.35.875-.35t.9.375q.375.375.375.875t-.375.875L3.55 12l7.35 7.35q.35.35.35.863t-.375.887q-.375.375-.875.375t-.875-.375Z"/></svg>
<span class="text-white text-md font-medium">
Return
</span>
</label>
</div>
</div>
</div>
{/if}

View File

@ -99,6 +99,7 @@ const handleUpvote = async (event) => {
'postId': postId,
'commentId': commentId,
'userId': data?.user?.id,
'path': 'upvote-comment'
};
upvoteButtonClicked = !upvoteButtonClicked;
@ -115,7 +116,7 @@ const handleUpvote = async (event) => {
} else {
upvoteCounter--;
}
const response = await fetch(data?.fastifyURL+'/upvote-comment', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -133,6 +134,7 @@ const handleDownvote = async (event) => {
const postData = {
'commentId': commentId,
'userId': data?.user?.id,
'path': 'downvote-comment'
};
downvoteButtonClicked = !downvoteButtonClicked;
@ -151,7 +153,7 @@ const handleDownvote = async (event) => {
}
const response = await fetch(data?.fastifyURL+'/downvote-comment', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -184,12 +186,13 @@ const handleDownvote = async (event) => {
const postData = {
'userId': data?.user?.id,
'commentId': comment?.id,
'commentUser': comment?.user
'commentUser': comment?.user,
'path': 'delete-comment'
}
const response = await fetch(data?.fastifyURL+'/delete-comment', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -48,7 +48,7 @@
'description': inputValue};
const response = await fetch(data?.fastifyURL+'/feedback', {
const response = await fetch('/api/feedback', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -71,10 +71,11 @@
const postData = {'postId': posts?.id,
'userId': posts?.user
'userId': posts?.user,
'path': 'delete-post',
};
const response = await fetch(data?.fastifyURL+'/delete-post', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -113,6 +114,7 @@ const handleUpvote = async (event) => {
const postData = {
'postId': postId,
'userId': data?.user?.id,
'path': 'upvote',
};
upvoteButtonClicked[postId] = !upvoteButtonClicked[postId];
@ -132,7 +134,7 @@ const handleUpvote = async (event) => {
$postVote = {'id': postId, 'upvote': upvoteCounter[postId], 'downvote': downvoteCounter[postId], 'upvoteClicked': upvoteButtonClicked[postId], 'downvoteClicked': downvoteButtonClicked[postId]};
const response = await fetch(data?.fastifyURL+'/upvote', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -149,6 +151,7 @@ const handleDownvote = async (event) => {
const postData = {
'postId': postId,
'userId': data?.user?.id,
'path': 'downvote',
};
downvoteButtonClicked[postId] = !downvoteButtonClicked[postId];
@ -168,7 +171,7 @@ const handleDownvote = async (event) => {
$postVote = {'id': postId, 'upvote': upvoteCounter[postId], 'downvote': downvoteCounter[postId], 'upvoteClicked': upvoteButtonClicked[postId], 'downvoteClicked': downvoteButtonClicked[postId]};
const response = await fetch(data?.fastifyURL+'/downvote', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'

View File

@ -30,10 +30,11 @@
'priceWhenCreated': currentPrice?.toFixed(2),
'condition': values?.at(0) < 0 ? 'below' : 'above',
'targetPrice': displayPrice,
'path': 'create-price-alert'
}
// Make the POST request to the endpoint
const response = await fetch(data?.fastifyURL+'/create-price-alert', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -1,492 +0,0 @@
<script lang='ts'>
import toast from 'svelte-french-toast';
import {currentPortfolioPrice, displayCompanyName, screenWidth, traded, stockTicker, etfTicker, cryptoTicker, assetType} from '$lib/store';
export let data;
export let availableCash;
export let holdingShares;
let fastifyURL = import.meta.env.VITE_USEAST_FASTIFY_URL;
let estimatedTotal = 0;
let numberOfShares = 0;
let commissionPrice = 0;
let displayTab = 'changeOrder';
function handleMaxOrder()
{
numberOfShares = holdingShares;
estimatedTotal = (numberOfShares * $currentPortfolioPrice);
}
function handleInputChange(event) {
const inputValue = event.target.value;
if (!isNaN(inputValue) && Number(inputValue) >= 0) {
numberOfShares = Number(inputValue);
} else {
numberOfShares = 0;
}
estimatedTotal = numberOfShares * $currentPortfolioPrice;
}
function handleAppendNumber(num) {
// Append the clicked number to the current numberOfShares
numberOfShares = numberOfShares * 10 + num;
}
function handleRemoveNumber() {
// Remove the last digit from numberOfShares or set to zero if NaN
const numStr = numberOfShares.toString();
numberOfShares = numStr.length > 1 ? parseInt(numStr.slice(0, -1), 10) : 0;
}
function changeTab() {
if(displayTab === 'changeOrder' && numberOfShares > 0 && numberOfShares <= holdingShares)
{
displayTab = 'sellOrder';
}
else if (displayTab === 'sellOrder')
{
displayTab = 'changeOrder';
}
else {
toast.error('Please check your Input values again.', {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
}
async function handleSellOrder()
{
const postData = {
'userId': data?.user?.id,
'symbol': $assetType === 'stock' ? $stockTicker : $assetType === 'etf' ? $etfTicker : $cryptoTicker,
'assetType': $assetType,
'name': $displayCompanyName,
'numberOfShares': numberOfShares,
'estimatedTotal': estimatedTotal,
'soldPrice': $currentPortfolioPrice,
}
// Make the POST request to the endpoint
const response = await fetch(fastifyURL+'/sell-stock', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
});
const output = (await response.json())?.message;
if (output === 'success')
{
toast.success(`Successfully sold ${numberOfShares} ${$stockTicker} shares`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
numberOfShares = 0;
estimatedTotal = 0;
displayTab = 'changeOrder';
$traded = true;
const closePopup = document.getElementById("sellTradeModal");
closePopup?.dispatchEvent(new MouseEvent('click'))
}
else if (output === 'failure')
{
toast.error(`Something went wrong. Please try again.`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
else if (output === 'marketClosed')
{
toast.error(`The market is closed. Please try again later.`, {
style: 'border-radius: 200px; background: #333; color: #fff;'
});
}
}
$: {
if(numberOfShares)
{
estimatedTotal = (numberOfShares * $currentPortfolioPrice);
}
}
</script>
<svelte:head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
</svelte:head>
{#if $screenWidth >=640}
<!--Start Trade Modal-->
<input type="checkbox" id="sellTradeModal" class="modal-toggle" />
<dialog id="sellTradeModal" class="modal modal-bottom sm:modal-middle">
<label for="sellTradeModal" class="cursor-pointer modal-backdrop bg-[#fff] bg-opacity-[0.08]"></label>
<div class="modal-box w-full bg-[#000] border border-slate-600 h-[500px]" >
<!--Start Trade Modal-->
<div class="flex flex-col w-full">
{#if displayTab === 'changeOrder'}
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-gray-900 mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $assetType === 'etf' ? $etfTicker : $cryptoTicker}.png`} />
</div>
<span class="mb-1">
{$displayCompanyName?.length > 30 ? $displayCompanyName?.slice(0, 30) + "..." : $displayCompanyName}
</span>
<span class="mb-1 text-sm font-medium">
Current Price: ${$currentPortfolioPrice}
</span>
</div>
<div class="flex justify-between items-center mt-2">
<div class="flex flex-col sm:w-64">
<label class="label font-medium">
<span class="text-white label-text">Number of Shares</span>
</label>
<input bind:value={numberOfShares} on:input={handleInputChange} type="text" placeholder="Number of Shares" class="text-slate-200 input {numberOfShares > holdingShares ? 'input-error' : ''} bg-gray-900 input-md w-54 sm:w-full max-w-xs" />
</div>
<label on:click={handleMaxOrder} class="mt-9 btn bg-[#000] hover:bg-[#fff] hover:text-black text-white cursor-pointer sm:px-5 border rounded-lg">
Show Max
</label>
</div>
<span class="{numberOfShares <= holdingShares ? 'hidden' : ''} label-text-alt text-error mt-1">
Not enough shares to sell the amount
</span>
<div class="flex justify-between items-center mt-8 w-full">
<div class="flex flex-col sm:flex-row text-blue-400 text-sm font-medium">
<span class="mr-0 sm:mr-1">
Holding Shares:
</span>
<span>
{holdingShares}
</span>
</div>
<div class="flex flex-col sm:flex-row text-blue-400 text-sm">
<span class="mr-0 sm:mr-1">
Estimated Total:
</span>
<span>
{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</span>
</div>
</div>
<div class="w-5/6 max-w-lg pt-10 m-auto pb-20">
<button on:click={changeTab} class="btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Preview Sell Order
</button>
</div>
{:else if displayTab === 'sellOrder'}
<label on:click={changeTab} class="{$screenWidth > 640 ? 'hidden' : ''} rounded-full left-0 -top-4 relative flex justify-center items-center w-10 h-10 hover:bg-gray-600 hover:bg-opacity-[0.4]">
<svg class="w-7 h-7" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(-90 12 12)"><path fill="white" d="M13 7.828V20h-2V7.828l-5.364 5.364l-1.414-1.414L12 4l7.778 7.778l-1.414 1.414L13 7.828Z"/></g></svg>
</label>
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-gray-900 mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $etfTicker}.png`} />
</div>
<span class="mb-1 font-medium text-xl">
Selling {numberOfShares} shares of {$displayCompanyName?.length > 30 ? $displayCompanyName?.slice(0, 30) + "..." : $displayCompanyName}
</span>
</div>
<table class="table table-sm table-compact mt-3 text-start flex justify-start items-center w-full px-3 m-auto">
<tbody>
<!-- row 1 -->
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Price per Share</td>
<td class="bg-[#000] whitespace-normal">${$currentPortfolioPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Number of Shares</td>
<td class="bg-[#000] whitespace-normal">{numberOfShares}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Commission</td>
<td class="bg-[#000] whitespace-normal">${commissionPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Estimated Total</td>
<td class="bg-[#000] whitespace-normal">{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Available Cash After</td>
<td class="bg-[#000] whitespace-normal">{(availableCash+estimatedTotal-commissionPrice)?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
</tbody>
</table>
<div class="max-w-lg pt-10 m-auto pb-5 flex flex-row justify-center items-center">
<button on:click={changeTab} class="w-3/4 mr-8 btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md rounded-lg text-white font-bold text-md">
Change Order
</button>
<button on:click={handleSellOrder} class="w-3/4 btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md rounded-lg text-white font-bold text-md">
Sell
</button>
</div>
{/if}
</div>
<!--End Trade Modal-->
</div>
</dialog>
{:else}
<!--Start Drawer Sidewise for mobile-->
<div class="drawer drawer-end z-40">
<input id="sellTradeModal" type="checkbox" class="drawer-toggle"/>
<div class="drawer-side">
<div class="modal-box overflow-hidden rounded-xl bg-[#000] min-h-screen w-screen">
<div class="flex flex-col w-full mt-14">
{#if displayTab === 'changeOrder'}
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-gray-900 mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $etfTicker}.png`} />
</div>
<span class="mb-1">
{$displayCompanyName?.length > 30 ? $displayCompanyName?.slice(0, 30) + "..." : $displayCompanyName}
</span>
<span class="mb-1 text-sm font-medium">
Current Price: ${$currentPortfolioPrice}
</span>
</div>
<div class="flex justify-between items-center mt-2">
<div class="flex flex-col sm:w-64">
<label class="label font-medium">
<span class="text-white label-text">Number of Shares</span>
</label>
<input inputmode="tel" pattern="[0-9]*" bind:value={numberOfShares} on:input={handleInputChange} type="text" placeholder="Number of Shares" class="text-slate-200 input {numberOfShares > holdingShares ? 'input-error' : ''} bg-gray-900 input-md w-54 sm:w-full max-w-xs" />
</div>
<label on:click={handleMaxOrder} class="mt-9 btn bg-[#000] hover:bg-[#fff] hover:text-black text-white cursor-pointer sm:px-5 border rounded-lg">
Show Max
</label>
</div>
<span class="{numberOfShares <= holdingShares ? 'hidden' : ''} label-text-alt text-error mt-1">
Not enough shares to sell the amount
</span>
<div class="flex justify-between items-center mt-8 w-full">
<div class="flex flex-col sm:flex-row text-blue-400 text-sm font-medium">
<span class="mr-0 sm:mr-1">
Holding Shares:
</span>
<span>
{holdingShares}
</span>
</div>
<div class="flex flex-col sm:flex-row text-blue-400 text-sm">
<span class="mr-0 sm:mr-1">
Estimated Total:
</span>
<span>
{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</span>
</div>
</div>
<!--
<div class="grid grid-cols-3 gap-x-6 gap-y-2 text-white font-bold text-lg m-auto mt-14">
{#each Array.from({ length: 10 }, (_, index) => index+1) as num}
{#if num < 10}
<label on:click={() => handleAppendNumber(num)} class="rounded-full flex justify-center items-center w-16 h-16">
{num}
</label>
{/if}
{/each}
<div class="rounded-full flex justify-center items-center w-16 h-16">
</div>
<div class="rounded-full flex justify-center items-center w-16 h-16 hover:bg-gray-600 hover:bg-opacity-[0.4]">
0
</div>
<label on:click={() => handleRemoveNumber()} class="rounded-full flex justify-center items-center w-16 h-16 hover:bg-gray-600 hover:bg-opacity-[0.4]">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(-90 12 12)"><path fill="white" d="M13 7.828V20h-2V7.828l-5.364 5.364l-1.414-1.414L12 4l7.778 7.778l-1.414 1.414L13 7.828Z"/></g></svg>
</label>
</div>
-->
<div class="w-5/6 max-w-lg pt-16 m-auto pb-20">
<button on:click={changeTab} class="btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Preview Sell Order
</button>
</div>
{:else if displayTab === 'sellOrder'}
<div class="text-white text-md flex flex-col flex-shrink-0">
<div class="rounded-full w-10 h-10 relative bg-gray-900 mb-2">
<img class="rounded-full w-6 h-6 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" src={`https://financialmodelingprep.com/image-stock/${$assetType === 'stock' ? $stockTicker : $etfTicker}.png`} />
</div>
<span class="mb-1 font-medium text-xl">
Selling {numberOfShares} shares of {$displayCompanyName?.length > 30 ? $displayCompanyName?.slice(0, 30) + "..." : $displayCompanyName}
</span>
</div>
<table class="table table-sm table-compact mt-3 text-start flex justify-start items-center w-full px-3 m-auto">
<tbody>
<!-- row 1 -->
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Price per Share</td>
<td class="bg-[#000] whitespace-normal">${$currentPortfolioPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Number of Shares</td>
<td class="bg-[#000] whitespace-normal">{numberOfShares}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Commission</td>
<td class="bg-[#000] whitespace-normal">${commissionPrice}</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Estimated Total</td>
<td class="bg-[#000] whitespace-normal">{estimatedTotal?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
<tr class="text-white" style="font-size: 0.8rem">
<td class="text-start bg-[#000] text-white font-medium">Available Cash After</td>
<td class="bg-[#000] whitespace-normal">{(availableCash+estimatedTotal-commissionPrice)?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
</td>
</tr>
</tbody>
</table>
<div class="w-5/6 max-w-lg pt-10 m-auto pb-5 flex flex-col items-center">
<button on:click={changeTab} class="btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Change Order
</button>
<button on:click={handleSellOrder} class="mt-6 btn bg-[#000] hover:bg-[#fff] hover:text-black border border-slate-500 btn-md w-full rounded-lg m-auto text-white font-bold text-md">
Sell Order
</button>
</div>
{/if}
</div>
<label for="sellTradeModal" class="absolute left-6 top-4 sm:hidden">
<svg class="w-6 h-6 inline-block mb-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#fff" d="M9.125 21.1L.7 12.7q-.15-.15-.213-.325T.425 12q0-.2.063-.375T.7 11.3l8.425-8.425q.35-.35.875-.35t.9.375q.375.375.375.875t-.375.875L3.55 12l7.35 7.35q.35.35.35.863t-.375.887q-.375.375-.875.375t-.875-.375Z"/></svg>
<span class="text-white text-md font-medium">
Return
</span>
</label>
</div>
</div>
</div>
{/if}

View File

@ -1,83 +0,0 @@
let fastifyURL = import.meta.env.VITE_USEAST_FASTIFY_URL; //import.meta.env.VITE_EU_FASTIFY_URL;
export const trackPageVisit = async (path, userAgent) => {
const postData = {
type: "trackPageVisit",
path: path,
userAgent: userAgent,
};
const response = await fetch(fastifyURL + "/mixpanel", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
};
export const trackPageDuration = async (path, userAgent, time) => {
const postData = {
type: "trackPageDuration",
path: path,
time: time,
userAgent: userAgent,
};
const response = await fetch(fastifyURL + "/mixpanel", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
};
export const trackPageError = async (path, userAgent, status, message) => {
const postData = {
type: "trackPageError",
path: path,
status: status,
message: message,
userAgent: userAgent,
};
const response = await fetch(fastifyURL + "/mixpanel", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
};
export const trackAsset = async (symbol, assetType) => {
const postData = {
type: "trackAsset",
symbol: symbol,
assetType: assetType,
};
const response = await fetch(fastifyURL + "/mixpanel", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
};
export const trackButtonClick = async (name) => {
const postData = {
type: "trackButton",
name: name,
};
const response = await fetch(fastifyURL + "/mixpanel", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
};

View File

@ -1,9 +1,9 @@
// lib/workers/test.ts
async function loadNotifications(fastifyURL: string, userId: string) {
async function loadNotifications(userId: string) {
const postData = { userId: userId };
const response = await fetch(fastifyURL + "/get-notifications", {
const response = await fetch("/api/get-notifications", {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -17,12 +17,9 @@ async function loadNotifications(fastifyURL: string, userId: string) {
onmessage = async (event: MessageEvent) => {
const data = event.data?.message;
const fastifyURL = data?.fastifyURL;
const userId = data?.userId;
try {
const [notificationList] = await Promise.all([
loadNotifications(fastifyURL, userId),
]);
const [notificationList] = await Promise.all([loadNotifications(userId)]);
const numberOfUnreadNotification = notificationList?.length;
const hasUnreadElement = notificationList?.length !== 0 ? true : false;

View File

@ -1,30 +0,0 @@
async function loadTwitchStatus(fastifyURL: string) {
// make the GET request to the endpoint
const response = await fetch(fastifyURL + "/get-twitch-status", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = (await response.json())?.items;
return output;
}
onmessage = async (event: MessageEvent) => {
const data = event.data?.message;
const fastifyURL = data?.fastifyURL;
try {
const twitchStatus = await loadTwitchStatus(fastifyURL);
const output = { twitchStatus };
postMessage({ message: "success", output });
} catch (e) {
postMessage({ message: "error", e });
}
// Sending data back to the main thread
//postMessage({ message: 'Data received in the worker', ticker, apiURL });
};
export {};

View File

@ -132,7 +132,7 @@ const loadWorker = async () => {
if ('serviceWorker' in navigator) {
const SyncWorker = await import('$lib/workers/notificationWorker?worker');
syncWorker = new SyncWorker.default();
syncWorker.postMessage({ message: {'fastifyURL': data?.fastifyURL, 'userId': data?.user?.id }});
syncWorker.postMessage({ message: {'userId': data?.user?.id }});
syncWorker.onmessage = handleMessage;
} else {
// Fallback logic here
@ -147,7 +147,7 @@ async function fallbackWorker() {
console.log('Fallback worker activated');
const postData = {'userId': data?.user?.id};
const response = await fetch(data?.fastifyURL+'/get-notifications', {
const response = await fetch('/api/get-notifications', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -162,17 +162,6 @@ async function fallbackWorker() {
}
/*
const loadTwitchWorker = async () => {
const SyncWorker = await import('$lib/workers/twitchStatusWorker?worker');
syncWorker = new SyncWorker.default();
syncWorker.postMessage({ message: {'fastifyURL': fastifyURL }});
syncWorker.onmessage = handleTwitchMessage;
};
*/
let Cookie;
$showCookieConsent = typeof data?.cookieConsent !== 'undefined' ? false : true;

View File

@ -0,0 +1,21 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const data = await request.json();
const { fastifyURL } = locals;
// Destructure 'path' from data and collect the rest
const { path, ...restData } = data;
const response = await fetch(`${fastifyURL}/${path}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
// Pass the rest of the data (excluding path) in the body
body: JSON.stringify(restData),
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,18 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const data = await request.json();
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/feedback", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,18 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const data = await request.json();
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/get-all-comments", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,16 @@
import type { RequestHandler } from "./$types";
export const GET: RequestHandler = async ({ locals }) => {
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/get-community-stats", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,16 @@
import type { RequestHandler } from "./$types";
export const GET: RequestHandler = async ({ locals }) => {
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/get-moderators", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,18 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const data = await request.json();
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/get-notifications", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,18 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const data = await request.json();
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/get-one-post", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -0,0 +1,18 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const data = await request.json();
const { fastifyURL } = locals;
const response = await fetch(fastifyURL + "/get-post", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const output = await response.json();
return new Response(JSON.stringify(output));
};

View File

@ -23,7 +23,6 @@
let moderators ;
let communityStats;
let discordData = [];
let tickerMentioning = [];
let posts = null;
@ -73,8 +72,7 @@ const getModerators = async () => {
output = cachedData;
} else {
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-moderators', {
const response = await fetch('/api/get-moderators', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
@ -89,32 +87,7 @@ const getModerators = async () => {
return output;
};
/*
const getTickerMentioning = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache('', 'getTickerMentioning');
if (cachedData) {
output = cachedData;
} else {
// make the POST request to the endpoint
const response = await fetch(apiURL + '/ticker-mentioning', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
output = await response.json();
setCache('', output, 'getTickerMentioning');
}
return output;
};
*/
const getCommunityStats = async () => {
let output;
@ -124,8 +97,7 @@ const getCommunityStats = async () => {
output = cachedData;
} else {
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-community-stats', {
const response = await fetch('/api/get-community-stats', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
@ -167,7 +139,7 @@ async function getPost() {
}
// Make the POST request to the endpoint
const response = await fetch(data?.fastifyURL+'/get-post', {
const response = await fetch('/api/get-post', {
method: 'POST',
headers: {
'Content-Type': 'application/json',

View File

@ -39,6 +39,7 @@
const postData = {
'postId': postId,
'userId': data?.user?.id,
'path': 'upvote',
};
upvoteButtonClicked[postId] = !upvoteButtonClicked[postId];
@ -58,7 +59,7 @@
$postVote = {'id': postId, 'upvote': upvoteCounter[postId], 'downvote': downvoteCounter[postId], 'upvoteClicked': upvoteButtonClicked[postId], 'downvoteClicked': downvoteButtonClicked[postId]};
const response = await fetch(data?.fastifyURL+'/upvote', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -75,6 +76,7 @@
const postData = {
'postId': postId,
'userId': data?.user?.id,
'path': 'downvote',
};
downvoteButtonClicked[postId] = !downvoteButtonClicked[postId];
@ -94,7 +96,7 @@
$postVote = {'id': postId, 'upvote': upvoteCounter[postId], 'downvote': downvoteCounter[postId], 'upvoteClicked': upvoteButtonClicked[postId], 'downvoteClicked': downvoteButtonClicked[postId]};
const response = await fetch(data?.fastifyURL+'/downvote', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -153,10 +155,11 @@
const postData = {
postId: data?.getPostId,
userId: data?.user?.id
userId: data?.user?.id,
path: 'delete-post'
};
const response = await fetch(data?.fastifyURL+'/delete-post', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -207,7 +210,7 @@
const postData = {'postId': data?.getPostId};
// make the GET request to the endpoint
const response = await fetch(data?.fastifyURL+'/get-all-comments', {
const response = await fetch('/api/get-all-comments', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -232,7 +235,7 @@
} else {
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-moderators', {
const response = await fetch('/api/get-moderators', {
method: 'GET',
headers: {
"Content-Type": "application/json"

View File

@ -2,7 +2,7 @@ import { cachedPosts } from "$lib/store";
import { get } from "svelte/store";
export const load = async ({ parent, params }) => {
export const load = async ({ params, fetch }) => {
async function getOnePost() {
// Get the current value of cachedPosts
const cachedValue = get(cachedPosts);
@ -12,15 +12,14 @@ export const load = async ({ parent, params }) => {
cachedValue?.posts?.find((item) => item?.id === params.postId) ?? {};
// If the post is found in the cache, return it
if (Object.keys(output).length !== 0) {
if (Object?.keys(output)?.length !== 0) {
return output;
}
const { fastifyURL } = await parent();
// If the post is not found in the cache, fetch it from the endpoint
const postData = { postId: params.postId };
const response = await fetch(fastifyURL + "/get-one-post", {
const response = await fetch("/api/get-one-post", {
method: "POST",
headers: {
"Content-Type": "application/json",
@ -31,7 +30,7 @@ export const load = async ({ parent, params }) => {
const result = await response.json();
// Assuming the result contains an 'items' array
return result.items;
return result?.items;
}
const getPostId = async () => {

View File

@ -339,7 +339,7 @@ const getModerators = async () => {
} else {
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-moderators', {
const response = await fetch('/api/get-moderators', {
method: 'GET',
headers: {
"Content-Type": "application/json"
@ -370,10 +370,10 @@ const getUserStats = async () => {
output = cachedData;
} else {
const postData = {'userId': data?.user?.id};
const postData = {'userId': data?.user?.id, 'path': 'get-user-stats'};
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-user-stats', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -437,10 +437,11 @@ async function getPost() {
startPage: currentPage,
seenPostId: seenPostId.length === 0 ? [] : seenPostId,
userId: data?.user?.id,
path: 'get-post',
};
// Make the POST request to the endpoint
const response = await fetch(data?.fastifyURL+'/get-post', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -42,9 +42,9 @@ let noPostMore = false;
const getUserData = async() => {
const postData = {'userId': data?.userId};
const postData = {'userId': data?.userId, 'path': 'get-user-data'};
const response = await fetch(data?.fastifyURL+'/get-user-data', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -93,10 +93,10 @@ const getUserStats = async () => {
output = cachedData;
} else {
const postData = {'userId': data?.userId};
const postData = {'userId': data?.userId, 'path': 'get-user-stats'};
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-user-stats', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -123,8 +123,7 @@ const getModerators = async () => {
output = cachedData;
} else {
// make the POST request to the endpoint
const response = await fetch(data?.fastifyURL + '/get-moderators', {
const response = await fetch('/api/get-moderators', {
method: 'GET',
headers: {
"Content-Type": "application/json"
@ -155,10 +154,11 @@ async function getPost() {
startPage: currentPage,
seenPostId: seenPostId.length === 0 ? [] : seenPostId,
userId: data?.userId,
path: 'get-post'
};
// Make the POST request to the endpoint
const response = await fetch(data?.fastifyURL+'/get-post', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -50,7 +50,6 @@ async function loadSearchData() {
let userWatchList = data?.getUserWatchlist ?? [];
let isTickerIncluded;
let userPortfolio = data?.getUserPortfolio ?? [];
let holdingShares = 0;
let availableCash = 0;
@ -155,9 +154,10 @@ async function toggleUserWatchlist(watchListId: string) {
'userId': data?.user?.id,
'watchListId': watchListId,
'ticker': $cryptoTicker,
'path': 'update-watchlist'
};
const response = await fetch(data?.fastifyURL + '/update-watchlist', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -188,23 +188,7 @@ async function toggleUserWatchlist(watchListId: string) {
}
async function fetchPortfolio()
{
const postData = {'userId': data?.user?.id};
const response = await fetch(data?.fastifyURL+'/get-portfolio-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
});
userPortfolio = (await response.json())?.items;
}
@ -280,10 +264,7 @@ async function websocketRealtimeData() {
let LoginPopup;
let BuyTrade;
let SellTrade;
let PriceAlert;
let AddPortfolio;
onMount(async () => {
@ -390,27 +371,11 @@ $: {
}
}
$: {
if(userPortfolio) {
availableCash = userPortfolio?.at(0)?.availableCash;
const userHoldingList = userPortfolio?.at(0)?.holdings || [];
const stockIndex = userHoldingList?.findIndex(stock => stock?.symbol === $cryptoTicker);
if (stockIndex !== -1)
{
holdingShares = userHoldingList[stockIndex]['numberOfShares'];
}
else {
holdingShares = 0;
}
}
}
$: {
if(typeof window !== 'undefined' && $traded && data?.user && $cryptoTicker?.length !== 0)
{
fetchPortfolio();
$traded = false;
}
}

View File

@ -1,572 +0,0 @@
<script lang='ts'>
import { numberOfUnreadNotification, screenWidth, isOpen } from '$lib/store';
import { onMount, onDestroy } from 'svelte';
import UpgradeToPro from '$lib/components/UpgradeToPro.svelte';
import { abbreviateNumber } from '$lib/utils';
import { goto } from '$app/navigation';
export let data;
let isLoaded = false;
let rawData = []
let displayList = [];
let mostFrequentTicker;
let highestVolumeTicker;
let highestSizeTicker;
let highestAmountTicker;
let sumAmountTicker
let displayDate;
let scrollContainer;
let notFound = false;
let filterQuery = '';
function getLastDate(dateString) {
const date = new Date(dateString);
// Check if it is open
if ($isOpen) {
return date?.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
} else {
const dayOfWeek = date.getDay(); // 0 (Sunday) to 6 (Saturday)
// Check if it is a weekday (Monday to Friday)
if (dayOfWeek >= 1 && dayOfWeek <= 5) {
date.setDate(date.getDate());
} else {
// Find the last weekday of the week
// If it's Saturday, go back to Friday
// If it's Sunday, go back to Friday
if (dayOfWeek === 6) {
date.setDate(date.getDate() - 1);
} else if (dayOfWeek === 0) {
date.setDate(date.getDate() - 2);
}
}
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}
}
function formatTime(dateString) {
// Parse the date string to a Date object
const date = new Date(dateString);
// Extract hours, minutes, and seconds
let hours = date.getUTCHours();
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
// Determine AM/PM
const ampm = hours >= 12 ? 'PM' : 'AM';
// Convert hours from 24-hour to 12-hour format
hours = hours % 12;
hours = hours ? hours : 12; // the hour '0' should be '12'
// Format hours
const formattedHours = String(hours).padStart(2, '0');
// Format time as hh:mm:ss AM/PM
return `${formattedHours}:${minutes}:${seconds} ${ampm}`;
}
function findMostFrequentTicker(data) {
const tickerCountMap = new Map();
// Iterate through the data and update the count for each ticker
data?.forEach(item => {
const ticker = item?.symbol;
if (tickerCountMap?.has(ticker)) {
tickerCountMap?.set(ticker, tickerCountMap?.get(ticker) + 1);
} else {
tickerCountMap?.set(ticker, 1);
}
});
let maxTicker;
let maxCount = -1;
// Find the ticker with the highest count
tickerCountMap?.forEach((count, ticker) => {
if (count > maxCount) {
maxCount = count;
maxTicker = ticker;
}
});
return { ticker: maxTicker, count: maxCount };
}
function findHighestVolume(data) {
let maxVolume = -1;
let maxVolumeTicker = null;
// Iterate through the data and find the ticker with the highest volume
data?.forEach(item => {
const volume = parseInt(item?.volume); // Assuming volume is a string, parse it to an integer
if (volume > maxVolume) {
maxVolume = volume;
maxVolumeTicker = item?.symbol;
}
});
return { ticker: maxVolumeTicker, volume: maxVolume };
}
function findHighestSize(data) {
let maxSize = -1;
let maxSizeTicker = null;
// Iterate through the data and find the ticker with the highest cost basis
data?.forEach(item => {
if (item?.size > maxSize) {
maxSize = item?.size;
maxSizeTicker = item?.symbol;
}
});
return { ticker: maxSizeTicker, size: maxSize };
}
function findHighestAmount(data) {
let maxAmount = -1;
let maxAmountTicker = null;
// Iterate through the data and find the ticker with the highest cost basis
data?.forEach(item => {
if ((item?.volume*item?.price) > maxAmount) {
maxAmount = item?.volume*item?.price;
maxAmountTicker = item?.symbol;
}
});
return { ticker: maxAmountTicker, amount: maxAmount };
}
function sumTotalAmount(data) {
let totalAmount = 0;
let ticker;
// Iterate through the data and sum up the cost basis for each item
data?.forEach(item => {
totalAmount += item?.volume * item?.price;
});
return{ ticker: data?.at(0)?.symbol, totalAmount: totalAmount };
}
async function handleScroll() {
if (!scrollContainer) return;
const scrollThreshold = scrollContainer.scrollHeight * 0.8; // 80% of the div height
const isBottom = scrollContainer.scrollTop + scrollContainer.clientHeight >= scrollThreshold;
if (isBottom && displayList?.length !== rawData?.length) {
const nextIndex = displayList?.length;
const filteredNewResults = rawData?.slice(nextIndex, nextIndex + 25);
displayList = [...displayList, ...filteredNewResults];
}
}
onMount(() => {
rawData = data?.getDarkPoolFlow ?? [];
displayList = rawData?.slice(0,20) ?? []
displayDate = getLastDate(rawData?.at(0)?.date)
mostFrequentTicker = findMostFrequentTicker(rawData);
highestVolumeTicker = findHighestVolume(rawData);
highestSizeTicker = findHighestSize(rawData);
highestAmountTicker = findHighestAmount(rawData);
sumAmountTicker = sumTotalAmount(rawData);
if (data?.user?.tier === 'Pro') {
const attachScrollListener = () => {
if (scrollContainer) {
scrollContainer.addEventListener('scroll', handleScroll);
return true;
}
return false;
};
if (!attachScrollListener()) {
const observer = new MutationObserver(() => {
if (attachScrollListener()) {
observer.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
}
isLoaded = true;
})
onDestroy(async() => {
if (scrollContainer && data?.user?.tier === 'Pro') {
scrollContainer.removeEventListener('scroll', handleScroll);
};
})
function handleInput(event) {
filterQuery = event.target.value;
let newData = [];
setTimeout(() => {
if (filterQuery?.length !== 0) {
newData = [...rawData?.filter(item => item?.symbol === filterQuery?.toUpperCase())];
if (newData?.length !== 0) {
rawData = newData;
displayList = [...rawData?.slice(0, 100)];
notFound = false;
} else {
notFound = true;
rawData = data?.getDarkPoolFlow;
displayList = rawData?.slice(0, 100);
}
} else {
notFound = false;
rawData = data?.getDarkPoolFlow;
displayList = rawData?.slice(0, 100);
}
mostFrequentTicker = findMostFrequentTicker(rawData);
highestVolumeTicker = findHighestVolume(rawData);
highestSizeTicker = findHighestSize(rawData);
highestAmountTicker = findHighestAmount(rawData);
sumAmountTicker = sumTotalAmount(rawData);
}, 200);
}
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
const debouncedHandleInput = debounce(handleInput, 200);
let charNumber = 40;
$: {
if ($screenWidth < 640)
{
charNumber = 15;
}
else {
charNumber = 40;
}
}
</script>
<svelte:options immutable={true} />
<svelte:head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>
{$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} Dark Pool Flow · stocknear
</title>
<meta name="description" content={`Realtime Dark Pool Trades from Hedge Funds & Major Institutional Traders.`} />
<!-- Other meta tags -->
<meta property="og:title" content={`Dark Pool Flow · stocknear`}/>
<meta property="og:description" content={`Realtime Dark Pool Trades from Hedge Funds & Major Institutional Traders.`} />
<meta property="og:type" content="website"/>
<!-- Add more Open Graph meta tags as needed -->
<!-- Twitter specific meta tags -->
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content={`Dark Pool Flow · stocknear`}/>
<meta name="twitter:description" content={`Realtime Dark Pool Trades from Hedge Funds & Major Institutional Traders.`} />
<!-- Add more Twitter meta tags as needed -->
</svelte:head>
<body class="sm:fixed h-screen m-auto w-full max-w-screen">
<section class="w-full max-w-screen sm:max-w-6xl flex justify-center items-center m-auto pt-5 bg-[#09090B] ">
<div class="w-full m-auto mb-10 pl-3 pr-3">
<div class="text-sm breadcrumbs mb-5">
<ul>
<li><a href="/" class="text-gray-300">Home</a></li>
<li class="text-gray-300">Dark Pool Flow</li>
</ul>
</div>
<div class="flex flex-col sm:flex-row items-center w-full bg-[#262626] rounded-lg px-3">
<div class="flex flex-row items-center justify-center sm:justify-start mt-6 pb-5">
<div class="ml-3 flex flex-col items-start">
<span class="text-xs sm:text-sm italic text-white">
Live flow of {new Date(displayList?.at(0)?.date ?? null)?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })} (NYSE Time)
</span>
</div>
</div>
<!--Start Filter-->
<div class="sm:ml-auto w-full sm:w-fit">
<div class="relative flex flex-col sm:flex-row items-center">
<div class="relative w-full sm:w-fit pl-3 py-2 sm:py-1.5 sm:mr-5 mb-4 sm:mb-0 flex-auto text-center bg-[#313131] rounded-lg border border-gray-600">
<label class="flex flex-row items-center ">
<input
id="modal-search"
type="search"
class="text-white sm:ml-2 text-[1rem] placeholder-gray-300 border-transparent focus:border-transparent focus:ring-0 flex items-center justify-center w-full px-0 py-1 bg-inherit"
placeholder="Find by Symbol"
bind:value={filterQuery}
on:input={debouncedHandleInput}
autocomplete="off"
/>
<svg class="ml-auto h-7 w-7 sm:h-8 sm:w-8 inline-block mr-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#fff" d="m19.485 20.154l-6.262-6.262q-.75.639-1.725.989t-1.96.35q-2.402 0-4.066-1.663T3.808 9.503T5.47 5.436t4.064-1.667t4.068 1.664T15.268 9.5q0 1.042-.369 2.017t-.97 1.668l6.262 6.261zM9.539 14.23q1.99 0 3.36-1.37t1.37-3.361t-1.37-3.36t-3.36-1.37t-3.361 1.37t-1.37 3.36t1.37 3.36t3.36 1.37"/></svg>
</label>
{#if notFound === true}
<span class="absolute left-1 -bottom-6 label-text text-error text-[0.65rem] mt-2">
No Results Found
</span>
{/if}
</div>
<div class="py-2 sm:py-1.5 mb-5 sm:mb-0 flex-auto text-center bg-[#000] rounded-lg w-full sm:w-fit">
<label for="filterList" class="sm:flex sm:flex-row justify-center items-center cursor-pointer px-5">
<svg class="h-6 w-6 inline-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M32 144h448M112 256h288M208 368h96"/></svg>
<span class="m-auto text-[1rem] text-white ml-2 px-0 py-1 bg-inherit">
Filters
</span>
</label>
</div>
</div>
</div>
<!--End Filter-->
</div>
{#if isLoaded }
<div class="w-full mt-5 mb-10 m-auto flex justify-center items-center">
<div class="w-full mt-5 mb-10 m-auto flex justify-center items-center p-3 sm:p-0">
<div class="w-full grid grid-cols-2 lg:grid-cols-4 gap-y-3 gap-x-3 ">
<!--Start Most Traded-->
<div class="flex flex-row items-center flex-wrap w-full px-3 sm:px-5 bg-[#262626] shadow-lg rounded-lg h-20">
<div class="flex flex-col items-start">
<span class="font-medium text-gray-200 text-sm ">Most Traded Symbol</span>
<span class="text-start text-sm sm:text-[1rem] font-medium text-white mt-0.5">
<a href={"/stocks/"+mostFrequentTicker?.ticker} class="text-blue-400 ">
{mostFrequentTicker?.ticker}
</a>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(mostFrequentTicker?.count)}
</span>
</div>
</div>
<!--End Most Traded-->
<!--Start Highest Volume-->
<div class="flex flex-row items-center flex-wrap w-full px-3 sm:px-5 bg-[#262626] shadow-lg rounded-lg h-20">
<div class="flex flex-col items-start">
<span class="font-medium text-gray-200 text-sm ">Highest Volume</span>
<span class="text-start text-sm sm:text-[1rem] font-medium text-white mt-0.5">
<a href={"/stocks/"+highestVolumeTicker?.ticker} class="text-blue-400 ">
{highestVolumeTicker?.ticker}
</a>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(highestVolumeTicker?.volume)}
</span>
</div>
</div>
<!--End Highest Volume-->
<!--Start Highest Size-->
<div class="flex flex-row items-center flex-wrap w-full px-5 bg-[#262626] shadow-lg rounded-lg h-20">
<div class="flex flex-col items-start">
<span class="font-medium text-gray-200 text-sm ">Highest Size</span>
<span class="text-start text-sm sm:text-[1rem] font-medium text-white mt-0.5">
<a href={"/stocks/"+highestSizeTicker?.ticker} class="text-blue-400 ">
{highestSizeTicker?.ticker}
</a>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(highestSizeTicker?.size)}
</span>
</div>
</div>
<!--End Highest Size-->
<!--Start Amount-->
<div class="flex flex-row items-center flex-wrap w-full px-5 bg-[#262626] shadow-lg rounded-lg h-20">
<div class="flex flex-col items-start">
<span class="font-medium text-gray-200 text-sm ">Highest Amount</span>
<span class="text-start text-sm sm:text-[1rem] font-medium text-white mt-0.5">
<a href={"/stocks/"+highestAmountTicker?.ticker} class="text-blue-400 ">
{highestAmountTicker?.ticker}
</a>
{abbreviateNumber(highestAmountTicker?.amount, true)}
</span>
</div>
</div>
<!--End Amount-->
<!--Start Amount-->
<div class="flex flex-row items-center flex-wrap w-full px-5 bg-[#262626] shadow-lg rounded-lg h-20">
<div class="flex flex-col items-start">
<span class="font-medium text-gray-200 text-sm ">Total Amount</span>
<span class="text-start text-sm sm:text-[1rem] font-medium text-white mt-0.5">
<a href={"/stocks/"+sumAmountTicker?.ticker} class="text-blue-400 ">
{sumAmountTicker?.ticker}
</a>
{abbreviateNumber(sumAmountTicker?.totalAmount, true)}
</span>
</div>
</div>
<!--End Amount-->
</div>
</div>
</div>
<!-- Page wrapper -->
<div class="flex justify-center w-full m-auto h-full overflow-hidden">
<!-- Content area -->
<div bind:this={scrollContainer} class="mt-4 w-full overflow-x-auto overflow-y-auto h-[900px] rounded-lg">
<table class="table table-pin-cols table-pin-rows table-sm table-compact">
<thead>
<tr class="">
<td class="bg-[#161618] text-slate-300 font-bold text-xs text-start uppercase">Time</td>
<th class="bg-[#161618] font-bold text-slate-300 text-xs text-start uppercase">Company</th>
<td class="bg-[#161618] text-slate-300 font-bold text-xs text-start uppercase">Size</td>
<td class="bg-[#161618] text-slate-300 font-bold text-xs text-end uppercase">Volume</td>
<td class="bg-[#161618] text-slate-300 font-bold text-xs text-end uppercase">Price</td>
<td class="bg-[#161618] text-slate-300 font-bold text-xs text-end uppercase">Amount</td>
</tr>
</thead>
<tbody>
{#each displayList as item,index}
<!-- row -->
<tr class="w-full odd:bg-[#27272A] cursor-pointer {index+1 === displayList?.length && data?.user?.tier !== 'Pro' ? 'opacity-[0.1]' : ''}">
<td class="text-start text-sm font-medium text-white whitespace-nowrap">
{formatTime(item?.date)}
</td>
<td on:click|stopPropagation={() => goto(`/stocks/${item?.symbol}`)} class="text-sm text-start whitespace-nowrap">
<div class="flex flex-col items-start w-32 sm:w-fit">
<span class="text-blue-400">{item?.symbol}</span>
<span class="text-white">
{item?.name?.length > charNumber ? item?.name?.slice(0,charNumber) + "..." : item?.name}
</span>
</div>
</td>
<td class="text-start text-sm font-medium text-white">
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(item?.size)}
</td>
<td class="text-end text-sm font-medium text-white">
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(item?.volume)}
</td>
<td class="text-end text-sm font-medium text-white">
${item?.price}
</td>
<td class="text-end text-sm font-medium text-white">
{abbreviateNumber(item?.price*item?.volume,true)}
</td>
</tr>
{/each}
</tbody>
</table>
<!--<InfiniteLoading on:infinite={infiniteHandler} />-->
</div>
</div>
<div class="relative bottom-[400px] w-fit m-auto flex justify-center items-center">
<UpgradeToPro data={data} title="Get the recent Options Flow Data from Hedge Funds and major institutional traders to never miss out"/>
</div>
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<span class="loading loading-spinner loading-md"></span>
</label>
</div>
</div>
{/if}
</div>
</section>
</body>

View File

@ -1,77 +0,0 @@
import { getCache, setCache, isOpen } from "$lib/store";
const checkMarketHour = async () => {
const holidays = [
"2024-01-01",
"2024-01-15",
"2024-02-19",
"2024-03-29",
"2024-05-27",
"2024-06-19",
"2024-07-04",
"2024-09-02",
"2024-11-28",
"2024-12-25",
];
const currentDate = new Date().toISOString().split("T")[0];
// Get the current time in the ET time zone
const etTimeZone = "America/New_York";
const currentTime = new Date().toLocaleString("en-US", {
timeZone: etTimeZone,
});
// Determine if the NYSE is currently open or closed
const currentHour = new Date(currentTime).getHours();
const isWeekendValue =
new Date(currentTime).getDay() === 6 ||
new Date(currentTime).getDay() === 0;
const isBeforeMarketOpenValue =
currentHour < 9 ||
(currentHour === 9 && new Date(currentTime).getMinutes() < 30);
const isAfterMarketCloseValue = currentHour >= 16;
isOpen.set(
!(
isWeekendValue ||
isBeforeMarketOpenValue ||
isAfterMarketCloseValue ||
holidays?.includes(currentDate)
),
);
};
export const load = async ({ parent }) => {
checkMarketHour();
const { apiKey, apiURL, user } = await parent();
const getDarkPoolFlow = async () => {
let output;
const cachedData = getCache("", "getDarkPoolFlow");
if (cachedData) {
output = cachedData;
} else {
const response = await fetch(apiURL + "/dark-pool-flow", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
output = await response.json();
setCache("", output, "getDarkPoolFlow");
}
output = user?.tier !== "Pro" ? output?.slice(0, 6) : output;
return output;
};
// Make sure to return a promise
return {
getDarkPoolFlow: await getDarkPoolFlow(),
};
};

View File

@ -166,9 +166,10 @@ async function toggleUserWatchlist(watchListId: string) {
'userId': data?.user?.id,
'watchListId': watchListId,
'ticker': $etfTicker,
'path': 'update-watchlist'
};
const response = await fetch(data?.fastifyURL + '/update-watchlist', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -198,22 +199,7 @@ async function toggleUserWatchlist(watchListId: string) {
}
}
async function fetchPortfolio()
{
const postData = {'userId': data?.user?.id};
const response = await fetch(data?.fastifyURL+'/get-portfolio-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
});
userPortfolio = (await response.json())?.items;
}
@ -291,10 +277,7 @@ async function fetchPortfolio()
let LoginPopup;
let BuyTrade;
let SellTrade;
let PriceAlert;
let AddPortfolio;
onMount(async () => {
@ -406,34 +389,9 @@ async function fetchPortfolio()
}
}
$: {
if(userPortfolio) {
availableCash = userPortfolio?.at(0)?.availableCash;
const userHoldingList = userPortfolio?.at(0)?.holdings || [];
const stockIndex = userHoldingList?.findIndex(stock => stock?.symbol === $etfTicker);
if (stockIndex !== -1)
{
holdingShares = userHoldingList[stockIndex]['numberOfShares'];
}
else {
holdingShares = 0;
}
}
}
$: {
if(typeof window !== 'undefined' && $traded && data?.user && $etfTicker?.length !== 0)
{
fetchPortfolio();
$traded = false;
}
}
let charNumber = 50;

View File

@ -0,0 +1,22 @@
export const load = async ({ locals }) => {
const getFDACalendar = async () => {
const { apiURL, apiKey } = locals;
const response = await fetch(apiURL + "/fda-calendar", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getFDACalendar: await getFDACalendar(),
};
};

View File

@ -1,33 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getFDACalendar = async () => {
let output;
const cachedData = getCache("", "getFDACalendar");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const response = await fetch(apiURL + "/fda-calendar", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
output = await response.json();
setCache("", output, "getFDACalendar");
}
return output;
};
// Make sure to return a promise
return {
getFDACalendar: await getFDACalendar(),
};
};

View File

@ -0,0 +1,64 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ locals }) => {
const { apiURL, apiKey } = locals;
const getSP500HeatMap = async () => {
const postData = { index: "sp500" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/heatmaps", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
const getDowJonesHeatMap = async () => {
const postData = { index: "dowjones" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/heatmaps", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
const getNasdaqHeatMap = async () => {
const postData = { index: "nasdaq" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/heatmaps", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getSP500HeatMap: await getSP500HeatMap(),
getDowJonesHeatMap: await getDowJonesHeatMap(),
getNasdaqHeatMap: await getNasdaqHeatMap(),
};
};

View File

@ -1,97 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const { apiURL, apiKey } = await parent();
const getSP500HeatMap = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getSP500HeatMap");
if (cachedData) {
output = cachedData;
} else {
const postData = { index: "sp500" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/heatmaps", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getSP500HeatMap'
setCache("", output, "getSP500HeatMap");
}
return output;
};
const getDowJonesHeatMap = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getDowJonesHeatMap");
if (cachedData) {
output = cachedData;
} else {
const postData = { index: "dowjones" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/heatmaps", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getDowJonesHeatMap'
setCache("", output, "getDowJonesHeatMap");
}
return output;
};
const getNasdaqHeatMap = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getNasdaqHeatMap");
if (cachedData) {
output = cachedData;
} else {
const postData = { index: "nasdaq" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/heatmaps", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getNasdaqHeatMap'
setCache("", output, "getNasdaqHeatMap");
}
return output;
};
// Make sure to return a promise
return {
getSP500HeatMap: await getSP500HeatMap(),
getDowJonesHeatMap: await getDowJonesHeatMap(),
getNasdaqHeatMap: await getNasdaqHeatMap(),
};
};

View File

@ -1,511 +0,0 @@
<script lang='ts'>
import { goto} from '$app/navigation';
import { numberOfUnreadNotification } from '$lib/store';
import { onMount } from 'svelte';
import {getImageURL} from '$lib/utils';
import TopInvestors from '$lib/components/TopInvestors.svelte';
export let data;
let cloudFrontUrl = import.meta.env.VITE_IMAGE_URL;
let leaderboard = data?.getLeaderboard ?? [];
let isLoaded = true;
let currentDate = new Date();
const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
const currentMonthIndex = currentDate.getMonth();
let displayMonth = 'n/a';
if (currentMonthIndex >= 0 && currentMonthIndex < monthNames.length) {
displayMonth = monthNames[currentMonthIndex];
}
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is zero-based
let nextMonth = currentDate.getMonth() + 1; // Increment the month by 1
let nextYear = year;
if (nextMonth === 12) {
nextMonth = 0; // Reset to January (zero-based index)
nextYear++; // Increment the year if the current month is December
}
nextMonth = String(nextMonth + 1).padStart(2, '0'); // Month is zero-based
const day = '01';
let startDate = `${year}-${month}-${day}`; // Output: "yyyy-mm-01"
let endDate = `${nextYear}-${nextMonth}-${day}`;
let targetDate = new Date(endDate);
let days = '-';
let hours = '-';
let minutes = '-';
let seconds = '-';
const updateTime = () => {
// Get the current time in the Berlin timezone
const berlinTimeZone = 'Europe/Berlin';
const berlinCurrentTime = new Date().toLocaleString('en-US', { timeZone: berlinTimeZone });
const currentTime = new Date(berlinCurrentTime);
// Calculate the time difference between the current time and the target date
const timeDiff = targetDate - currentTime;
// Calculate the remaining days, hours, minutes, and seconds
const totalSeconds = Math.floor(timeDiff / 1000);
seconds = totalSeconds % 60;
minutes = Math.floor((totalSeconds % 3600) / 60);
hours = Math.floor((totalSeconds % (3600 * 24)) / 3600);
days = Math.floor(totalSeconds / (3600 * 24));
};
async function changeLeaderboard(event)
{
isLoaded = false;
const valueDate = event.target.value;
if (valueDate === 'aug2023')
{
startDate = '2023-08-01';
endDate = '2023-09-01'
}
else if (valueDate === 'sep2023')
{
startDate = '2023-09-01';
endDate = '2023-10-01'
}
else if (valueDate === 'oct2023')
{
startDate = '2023-10-01';
endDate = '2023-11-01'
}
else if (valueDate === 'nov2023')
{
startDate = '2023-11-01';
endDate = '2023-12-01'
}
else if (valueDate === 'dec2023')
{
startDate = '2023-12-01';
endDate = '2023-12-31'
}
else if (valueDate === 'jan2024')
{
startDate = '2024-01-01';
endDate = '2024-01-31'
}
else if (valueDate === 'feb2024')
{
startDate = '2024-02-01';
endDate = '2024-02-29'
}
else if (valueDate === 'march2024')
{
startDate = '2024-03-01';
endDate = '2024-03-31'
}
else if (valueDate === 'april2024')
{
startDate = '2024-04-01';
endDate = '2024-04-30'
}
else if (valueDate === 'may2024')
{
startDate = '2024-05-01';
endDate = '2024-05-31'
}
else if (valueDate === 'june2024')
{
startDate = '2024-06-01';
endDate = '2024-06-30'
}
await getLeaderboard();
isLoaded = true;
}
onMount(async () => {
const interval = setInterval(updateTime, 1000);
return () => {
clearInterval(interval);
};
});
async function getLeaderboard() {
const postData = {
'startDate': startDate,
'endDate': endDate,
};
const response = await fetch(data?.fastifyURL+'/leaderboard', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData),
});
const output = (await response.json())?.items;
leaderboard = output
?.filter(item => item.rank !== 0)
?.sort((a, b) => a.rank - b.rank)
}
</script>
<svelte:head>
<title> {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} Leaderboard Stocks · stocknear</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="description" content="Detailed information of the current leaderboard of our free monthly portfolio tournament.">
<!-- Other meta tags -->
<meta property="og:title" content="Leaderboard · stocknear"/>
<meta property="og:description" content="Detailed information of the current leaderboard of our free monthly portfolio tournament.">
<meta property="og:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>
<meta property="og:type" content="website"/>
<!-- Add more Open Graph meta tags as needed -->
<!-- Twitter specific meta tags -->
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content="Leaderboard · stocknear"/>
<meta name="twitter:description" content="Detailed information of the current leaderboard of our free monthly portfolio tournament.">
<meta name="twitter:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>
<!-- Add more Twitter meta tags as needed -->
</svelte:head>
<div class="w-full max-w-4xl overflow-hidden m-auto min-h-screen pt-5 mb-40 ">
<!--
<div class="text-sm breadcrumbs ml-4">
<ul>
<li><a href="/" class="text-gray-300">Home</a></li>
<li class="text-gray-300">Leaderboard</li>
</ul>
</div>
-->
<div class="w-full max-w-4xl m-auto sm:bg-[#09090B] sm:rounded-xl h-auto p-10 mt-3">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-10">
<!-- Start Column -->
<div>
<div class="flex flex-col justify-center items-center">
<h1 class="text-5xl text-white font-bold mb-4">
Leaderboard
</h1>
<h2 class="text-center text-2xl text-white font-medium mb-5">
{displayMonth} 2024 🚀
</h2>
</div>
<div class="rounded-lg text-white p-2 flex flex-col font-medium text-center">
<span class="text-white font-medium text-xl mb-3">
Tournament ends in
</span>
<div class="grid grid-flow-col gap-5 text-center m-auto auto-cols-max">
<div class="flex flex-col text-xs">
<span class="countdown font-mono text-xl">
<span style="--value:{days};"></span>
</span>
days
</div>
<div class="flex flex-col text-xs">
<span class="countdown font-mono text-xl">
<span style="--value:{hours};"></span>
</span>
hours
</div>
<div class="flex flex-col text-xs">
<span class="countdown font-mono text-xl">
<span style="--value:{minutes};"></span>
</span>
min
</div>
<div class="flex flex-col text-xs">
<span class="countdown font-mono text-xl">
<span style="--value:{seconds};"></span>
</span>
sec
</div>
</div>
</div>
</div>
<!-- End Column -->
<!-- Start Column -->
<div class="hidden sm:block relative m-auto mb-10 mb-10 sm:mb-0 sm:mt-0">
<svg class="w-40 -my-5" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="glow">
<feGaussianBlur stdDeviation="5" result="glow"/>
<feMerge>
<feMergeNode in="glow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<path fill="#1E40AF" d="M57.6,-58.7C72.7,-42.6,81.5,-21.3,82,0.5C82.5,22.3,74.7,44.6,59.7,60.1C44.6,75.6,22.3,84.3,0,84.3C-22.3,84.2,-44.6,75.5,-61.1,60.1C-77.6,44.6,-88.3,22.3,-87.6,0.7C-86.9,-20.8,-74.7,-41.6,-58.2,-57.7C-41.6,-73.8,-20.8,-85.2,0.2,-85.4C21.3,-85.6,42.6,-74.7,57.6,-58.7Z" transform="translate(100 100)" filter="url(#glow)" />
</svg>
<div class="absolute top-4">
<img class="w-24 h-fit ml-6" src={cloudFrontUrl+'/assets/dab_icon.png'} alt="logo" loading="lazy">
</div>
</div>
<!-- End Column -->
</div>
</div>
<div class="flex-col jusitfy-end items-center mt-10 ml-2 mb-10">
<div class="text-white text-md font-medium mr-2">
View all Leaderboard's
</div>
<div class="relative mt-4">
<select class="select text-white select-bordered select-sm w-48 p-0 pl-5 overflow-y-auto bg-[#2A303C]" on:change={changeLeaderboard}>
<option disabled>View the past months of winners</option>
<option value="june2024" selected>{displayMonth} 2024</option>
<option value="may2024">May 2024</option>
<option value="april2024">April 2024</option>
<option value="march2024">March 2024</option>
<option value="feb2024">February 2024</option>
<option value="jan2024">January 2024</option>
<option value="dec2023">December 2023</option>
<option value="nov2023">November 2023</option>
<option value="oct2023">October 2023</option>
<option value="sep2023">September 2023</option>
<option value="aug2023">August 2023</option>
</select>
</div>
</div>
{#if leaderboard?.length > 3 && isLoaded}
<h3 class="text-white font-bold text-4xl sm:text-5xl flex justify-center items-center mt-10">
Top 3 Investors
</h3>
<div class="flex flex-row justify-center items-center mt-10 text-white mb-10">
<!--Start Rank 2-->
<TopInvestors data={leaderboard[1]} rank={2} />
<!--End Rank 2-->
<!--Start Rank 1-->
<TopInvestors data={leaderboard[0]} rank={1}/>
<!--End Rank 1-->
<!--Start Rank 3-->
<TopInvestors data={leaderboard[2]} rank={3} />
<!--End Rank 3-->
</div>
{/if}
{#if isLoaded}
<!--Start if leaderboad is empty-->
{#if leaderboard?.length ===0}
<h3 class="text-gray-300 text-center pt-5 w-72 sm:w-full max-w-xl flex justify-center m-auto text-lg sm:text-xl font-medium pb-9">
No Participation in the Tournament yet. Be the first and start the game 🔥
</h3>
<a href={data?.user ? '/portfolio' : '/login'} rel="noopener noreferrer" class="w-64 flex mb-5 justify-center items-center m-auto btn text-white bg-purple-600 hover:bg-purple-500 transition duration-150 ease-in-out group">
Get Started
<span class="tracking-normal group-hover:translate-x-0.5 transition-transform duration-150 ease-in-out">
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(90 12 12)"><g fill="none"><path d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z"/><path fill="white" d="M13.06 3.283a1.5 1.5 0 0 0-2.12 0L5.281 8.939a1.5 1.5 0 0 0 2.122 2.122L10.5 7.965V19.5a1.5 1.5 0 0 0 3 0V7.965l3.096 3.096a1.5 1.5 0 1 0 2.122-2.122L13.06 3.283Z"/></g></g></svg>
</span>
</a>
{:else}
{#each (leaderboard?.length >=3 ? leaderboard?.slice(3,-1) : leaderboard) as item,index }
<div class="p-2 sm:hidden">
<div class="shadow-lg bg-[#09090B] w-full rounded-lg p-4 sm:p-3 flex flex-row items-center ">
<div class="flex flex-row items-center">
<span class="text-white text-sm mr-3">
{item?.rank}
</span>
<div class="flex-shrink-0 mr-3 rounded-full w-12 h-12 relative bg-[#09090B]">
<img style="clip-path: circle(50%);"
class="rounded-full w-10 h-10 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2"
src={item?.expand?.user?.avatar
? getImageURL(item?.expand?.user?.collectionId, item?.expand?.user?.id, item?.expand?.user?.avatar)
: `https://avatar.vercel.sh/${item?.expand?.user?.username}`}
alt="User avatar" />
</div>
</div>
<div class="flex flex-col sm:-mt-5 w-full">
<div class="flex flex-row items-center w-full">
<span class="text-white text-sm sm:text-md font-medium text-start mb-2 mr-auto mt-3">
{item?.expand?.user?.username > 7 ? item?.expand?.user?.username?.slice(0,7) + "..." : item?.expand?.user?.username}
</span>
<div class="flex flex-col items-end text-white text-sm">
${new Intl.NumberFormat("en", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(item?.accountValue)}
<div class="flex flex-row items-end">
{#if item?.overallReturn > 0}
<svg class="w-5 h-5 -mr-0.5 mt-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#37C97D" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#37C97D] text-md">
+{item?.overallReturn?.toFixed(2)}%
</span>
{:else if item?.overallReturn < 0}
<svg class="w-5 h-5 -mr-0.5 mt-0.5 rotate-180" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#FF2F1F" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#FF2F1F] text-md">
{item?.overallReturn?.toFixed(2)}%
</span>
{:else}
<svg class="w-2.5 h-2.5 inline-block mr-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path fill="#fff" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19z"/></g></svg>
<span class="text-white text-md">
{item?.overallReturn?.toFixed(2)}%
</span>
{/if}
</div>
</div>
</div>
</div>
</div>
</div>
{/each}
<!--Desktop-->
<table class="hidden sm:table table-sm table-compact shadow-md rounded-none sm:rounded-md w-full bg-[#09090B] border-bg-[#09090B] m-auto mt-4 mb-10">
<thead>
<tr class="">
<th class="text-white sm:font-medium text-sm shadow-md">Rank</th>
<th class="text-white sm:font-medium text-sm hidden sm:block text-center shadow-md">Portfolio Holds </th>
<th class="text-white sm:font-medium text-sm text-end shadow-md ">Account Value</th>
</tr>
</thead>
<tbody class="shadow-md">
{#each (leaderboard?.length >=3 ? leaderboard?.slice(3,-1) : leaderboard) as item,index}
<tr class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-[#27272A] cursor-pointer">
<td on:click={() => goto("/community/user/"+item?.user)} class="cursor-pointer">
<div class="flex flex-row items-center">
<span class="text-white text-sm mr-3">
{item?.rank}
</span>
<div class="rounded-full w-9 h-9 relative bg-gray-800 hover:ring-[2px] hover:ring-blue-700 ease-in-out duration-300">
<img style="clip-path: circle(50%);"
class="rounded-full w-8 h-8 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2"
src={item?.expand?.user?.avatar
? getImageURL(item?.expand?.user?.collectionId, item?.expand?.user?.id, item?.expand?.user?.avatar)
: `https://avatar.vercel.sh/${item?.expand?.user?.username}`}
alt="User avatar" />
</div>
<span class="ml-2 text-white text-xs sm:text-md">
{item?.expand?.user?.username > 7 ? item?.expand?.user?.username?.slice(0,7) + "..." : item?.expand?.user?.username}
</span>
</div>
</td>
<td class="font-medium hidden sm:block text-center text-white">
{item?.holdings?.length}
</td>
<td class="text-white text-md ">
<div class="flex flex-col items-end">
${new Intl.NumberFormat("en", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(item?.accountValue)}
<div class="flex flex-row items-end mt-1">
{#if item?.overallReturn > 0}
<svg class="w-5 h-5 -mr-0.5 mt-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#37C97D" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#37C97D] text-md">
+{item?.overallReturn?.toFixed(2)}%
</span>
{:else if item?.overallReturn < 0}
<svg class="w-5 h-5 -mr-0.5 mt-0.5 rotate-180" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#FF2F1F" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#FF2F1F] text-md">
{item?.overallReturn?.toFixed(2)}%
</span>
{:else}
<svg class="w-2.5 h-2.5 inline-block mr-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path fill="#fff" d="M3 19h18a1.002 1.002 0 0 0 .823-1.569l-9-13c-.373-.539-1.271-.539-1.645 0l-9 13A.999.999 0 0 0 3 19z"/></g></svg>
<span class="text-white text-md">
{item?.overallReturn?.toFixed(2)}%
</span>
{/if}
</div>
</div>
</td>
</tr>
{/each}
</tbody>
</table>
{/if}
<!--End if leaderboad is empty-->
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<span class="loading loading-spinner loading-md"></span>
</label>
</div>
</div>
{/if}
</div>

View File

@ -1,51 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ params, parent }) => {
const getLeaderboard = async () => {
const currentDate = new Date();
const year = currentDate.getFullYear();
const currentMonthIndex = currentDate.getMonth();
const nextMonthIndex = (currentMonthIndex + 1) % 12;
const nextYear = year + Math.floor((currentMonthIndex + 1) / 12);
const nextMonth = String(nextMonthIndex + 1).padStart(2, "0");
const startDate = `${year}-${String(currentMonthIndex + 1).padStart(2, "0")}-01`;
const endDate = `${nextYear}-${nextMonth}-01`;
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getLeaderboard");
if (cachedData) {
output = cachedData;
} else {
const { fastifyURL } = await parent();
const postData = {
startDate: startDate,
endDate: endDate,
};
const response = await fetch(fastifyURL + "/leaderboard", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
output = (await response.json())?.items
?.filter((item) => item.rank !== 0)
?.sort((a, b) => a.rank - b.rank);
// Cache the data for this specific tickerID with a specific name 'getLeaderboard'
setCache("", output, "getLeaderboard");
}
return output;
};
// Make sure to return a promise
return {
getLeaderboard: await getLeaderboard(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getAmexStocks = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "amex" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getAmexStocks: await getAmexStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getAmexStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getAmexStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "amex" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getAmexStocks'
setCache("", output, "getAmexStocks");
}
return output;
};
// Make sure to return a promise
return {
getAmexStocks: await getAmexStocks(),
};
};

View File

@ -0,0 +1,22 @@
export const load = async ({ locals }) => {
const getETFBitcoinList = async () => {
const { apiKey, apiURL } = locals;
const response = await fetch(apiURL + "/etf-bitcoin-list", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getETFBitcoinList: await getETFBitcoinList(),
};
};

View File

@ -1,35 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getETFBitcoinList = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getETFBitcoinList");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const response = await fetch(apiURL + "/etf-bitcoin-list", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getETFBitcoinList'
setCache("", output, "getETFBitcoinList");
}
return output;
};
// Make sure to return a promise
return {
getETFBitcoinList: await getETFBitcoinList(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getCanadianStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "CA" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getCanadianStocksUS: await getCanadianStocksUS(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getCanadianStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getCanadianStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "CA" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getCanadianStocksUS'
setCache("", output, "getCanadianStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getCanadianStocksUS: await getCanadianStocksUS(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getChineseStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "CN" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getChineseStocksUS: await getChineseStocksUS(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getChineseStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getChineseStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "CN" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getChineseStocksUS'
setCache("", output, "getChineseStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getChineseStocksUS: await getChineseStocksUS(),
};
};

View File

@ -0,0 +1,22 @@
export const load = async ({ locals }) => {
const getDelistedStocks = async () => {
const { apiKey, apiURL } = locals;
const response = await fetch(apiURL + "/delisted-companies", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getDelistedStocks: await getDelistedStocks(),
};
};

View File

@ -1,34 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getDelistedStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getDelistedStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const response = await fetch(apiURL + "/delisted-companies", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
output = await response.json();
setCache("", output, "getDelistedStocks");
}
return output;
};
// Make sure to return a promise
return {
getDelistedStocks: await getDelistedStocks(),
};
};

View File

@ -0,0 +1,22 @@
export const load = async ({ locals }) => {
const getDividendAristocrats = async () => {
const { apiKey, apiURL } = locals;
const response = await fetch(apiURL + "/dividend-aristocrats", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getDividendAristocrats: await getDividendAristocrats(),
};
};

View File

@ -1,33 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getDividendAristocrats = async () => {
// Get cached data for the specific tickerID
const cachedData = getCache("", "getDividendAristocrats");
if (cachedData) {
return cachedData;
} else {
const { apiKey, apiURL } = await parent();
const response = await fetch(apiURL + "/dividend-aristocrats", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getDividendAristocrats'
setCache("", output, "getDividendAristocrats");
return output;
}
};
// Make sure to return a promise
return {
getDividendAristocrats: await getDividendAristocrats(),
};
};

View File

@ -0,0 +1,22 @@
export const load = async ({ locals }) => {
const getDividendKings = async () => {
const { apiKey, apiURL } = locals;
const response = await fetch(apiURL + "/dividend-kings", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getDividendKings: await getDividendKings(),
};
};

View File

@ -1,33 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getDividendKings = async () => {
// Get cached data for the specific tickerID
const cachedData = getCache("", "getDividendKings");
if (cachedData) {
return cachedData;
} else {
const { apiKey, apiURL } = await parent();
const response = await fetch(apiURL + "/dividend-kings", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getDividendKings'
setCache("", output, "getDividendKings");
return output;
}
};
// Make sure to return a promise
return {
getDividendKings: await getDividendKings(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getDowJonesConstituentStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "dowjonesConstituent" };
const response = await fetch(apiURL + "/exchange-constituents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getDowJonesConstituentStocks: await getDowJonesConstituentStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getDowJonesConstituentStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getDowJonesConstituentStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "dowjonesConstituent" };
const response = await fetch(apiURL + "/exchange-constituents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
setCache("", output, "getDowJonesConstituentStocks");
}
return output;
};
// Make sure to return a promise
return {
getDowJonesConstituentStocks: await getDowJonesConstituentStocks(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getGermanStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "DE" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getGermanStocksUS: await getGermanStocksUS(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getGermanStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getGermanStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "DE" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getGermanStocksUS'
setCache("", output, "getGermanStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getGermanStocksUS: await getGermanStocksUS(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getIndianStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "IN" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getIndianStocksUS: await getIndianStocksUS(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getIndianStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getIndianStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "IN" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getIndianStocksUS'
setCache("", output, "getIndianStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getIndianStocksUS: await getIndianStocksUS(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getIsraeliStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "IL" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getIsraeliStocksUS: await getIsraeliStocksUS(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getIsraeliStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getIsraeliStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "IL" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getIsraeliStocksUS'
setCache("", output, "getIsraeliStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getIsraeliStocksUS: await getIsraeliStocksUS(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getJapaneseStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "JP" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getJapaneseStocksUS: await getJapaneseStocksUS(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getJapaneseStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getJapaneseStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "JP" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getJapaneseStocksUS'
setCache("", output, "getJapaneseStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getJapaneseStocksUS: await getJapaneseStocksUS(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getLargeCapStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "largeCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getLargeCapStocks: await getLargeCapStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getLargeCapStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getLargeCapStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "largeCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getLargeCapStocks'
setCache("", output, "getLargeCapStocks");
}
return output;
};
// Make sure to return a promise
return {
getLargeCapStocks: await getLargeCapStocks(),
};
};

View File

@ -0,0 +1,22 @@
export const load = async ({ locals }) => {
const getMagnificentSeven = async () => {
const { apiURL, apiKey } = locals;
const response = await fetch(apiURL + "/magnificent-seven", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getMagnificentSeven: await getMagnificentSeven(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getMagnificentSeven = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getMagnificentSeven");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const response = await fetch(apiURL + "/magnificent-seven", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
});
output = await response.json();
console.log(output);
// Cache the data for this specific tickerID with a specific name 'getMagnificentSeven'
setCache("", output, "getMagnificentSeven");
}
return output;
};
// Make sure to return a promise
return {
getMagnificentSeven: await getMagnificentSeven(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getMegaCapStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "megaCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getMegaCapStocks: await getMegaCapStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getMegaCapStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getMegaCapStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "megaCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getMegaCapStocks'
setCache("", output, "getMegaCapStocks");
}
return output;
};
// Make sure to return a promise
return {
getMegaCapStocks: await getMegaCapStocks(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getMicroCapStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "microCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getMicroCapStocks: await getMicroCapStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getMicroCapStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getMicroCapStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "microCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getMicroCapStocks'
setCache("", output, "getMicroCapStocks");
}
return output;
};
// Make sure to return a promise
return {
getMicroCapStocks: await getMicroCapStocks(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getMidCapStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "midCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getMidCapStocks: await getMidCapStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getMidCapStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getMidCapStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "midCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getMidCapStocks'
setCache("", output, "getMidCapStocks");
}
return output;
};
// Make sure to return a promise
return {
getMidCapStocks: await getMidCapStocks(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getNanoCapStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "nanoCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getNanoCapStocks: await getNanoCapStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getNanoCapStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getNanoCapStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "nanoCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getNanoCapStocks'
setCache("", output, "getNanoCapStocks");
}
return output;
};
// Make sure to return a promise
return {
getNanoCapStocks: await getNanoCapStocks(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getNasdaqConstituentsStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "nasdaqConstituent" };
const response = await fetch(apiURL + "/exchange-constituents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getNasdaqConstituentsStocks: await getNasdaqConstituentsStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getNasdaqConstituentsStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getNasdaqConstituentsStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "nasdaqConstituent" };
const response = await fetch(apiURL + "/exchange-constituents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
setCache("", output, "getNasdaqConstituentsStocks");
}
return output;
};
// Make sure to return a promise
return {
getNasdaqConstituentsStocks: await getNasdaqConstituentsStocks(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getNasdaqStocks = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "nasdaq" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getNasdaqStocks: await getNasdaqStocks(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getNasdaqStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getNasdaqStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "nasdaq" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getNasdaqStocks'
setCache("", output, "getNasdaqStocks");
}
return output;
};
// Make sure to return a promise
return {
getNasdaqStocks: await getNasdaqStocks(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getNyseStocks = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "nyse" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getNyseStocks: await getNyseStocks(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getNyseStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getNyseStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "nyse" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getNyseStocks'
setCache("", output, "getNyseStocks");
}
return output;
};
// Make sure to return a promise
return {
getNyseStocks: await getNyseStocks(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getAllREITs = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "reit" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getAllREITs: await getAllREITs(),
};
};

View File

@ -1,36 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getAllREITs = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getAllREITs");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "reit" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
setCache("", output, "getAllREITs");
}
return output;
};
// Make sure to return a promise
return {
getAllREITs: await getAllREITs(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getSmallCapStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "smallCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getSmallCapStocks: await getSmallCapStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getSmallCapStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getSmallCapStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "smallCap" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getSmallCapStocks'
setCache("", output, "getSmallCapStocks");
}
return output;
};
// Make sure to return a promise
return {
getSmallCapStocks: await getSmallCapStocks(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getSPConstituentsStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = { filterList: "sp500Constituent" };
const response = await fetch(apiURL + "/exchange-constituents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getSPConstituentsStocks: await getSPConstituentsStocks(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getSPConstituentsStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getSPConstituentsStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "sp500Constituent" };
const response = await fetch(apiURL + "/exchange-constituents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
setCache("", output, "getSPConstituentsStocks");
}
return output;
};
// Make sure to return a promise
return {
getSPConstituentsStocks: await getSPConstituentsStocks(),
};
};

View File

@ -0,0 +1,25 @@
export const load = async ({ locals }) => {
const getUKStocksUS = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "GB" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getUKStocksUS: await getUKStocksUS(),
};
};

View File

@ -1,37 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getUKStocksUS = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getUKStocksUS");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { filterList: "GB" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
setCache("", output, "getUKStocksUS");
}
return output;
};
// Make sure to return a promise
return {
getUKStocksUS: await getUKStocksUS(),
};
};

View File

@ -0,0 +1,24 @@
export const load = async ({ locals }) => {
const getXetraStocks = async () => {
const { apiURL, apiKey } = locals;
const postData = { filterList: "xetra" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getXetraStocks: await getXetraStocks(),
};
};

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getXetraStocks = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getXetraStocks");
if (cachedData) {
output = cachedData;
} else {
const { apiKey, apiURL } = await parent();
const postData = { filterList: "xetra" };
const response = await fetch(apiURL + "/filter-stock-list", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getXetraStocks'
setCache("", output, "getXetraStocks");
}
return output;
};
// Make sure to return a promise
return {
getXetraStocks: await getXetraStocks(),
};
};

View File

@ -1,130 +0,0 @@
<script lang="ts">
import InfiniteLoading from '$lib/components/InfiniteLoading.svelte';
import { numberOfUnreadNotification } from '$lib/store';
import { formatDate } from '$lib/utils';
export let data;
let rawData = data?.getPressRelease;
let news = rawData.slice(0,5) ?? [];
async function infiniteHandler({ detail: { loaded, complete } })
{
if (news?.length === rawData?.length) {
complete();
} else {
const nextIndex = news?.length;
const newArticles = rawData?.slice(nextIndex, nextIndex + 5);
news = [...news, ...newArticles];
loaded();
}
}
let videoId = null;
function checkIfYoutubeVideo(link) {
const url = new URL(link);
if (url.hostname === "www.youtube.com") {
const searchParams = url.searchParams;
searchParams.delete('t'); // Remove the "t" parameter
const videoIdMatch = url.search.match(/v=([^&]+)/);
if (videoIdMatch) {
return videoIdMatch[1];
}
} else {
return null;
}
}
</script>
<svelte:head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>
{$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} Today's Crypto News and Breaking Stories · stocknear
</title>
<meta name="description" content={`Get the latest crypto news and breaking stories from the world's best finance and investing websites.`} />
<!-- Other meta tags -->
<meta property="og:title" content={`Today's Crypto News and Breaking Stories · stocknear`}/>
<meta property="og:description" content={`Get the latest crypto news and breaking stories from the world's best finance and investing websites.`} />
<meta property="og:type" content="website"/>
<!-- Add more Open Graph meta tags as needed -->
<!-- Twitter specific meta tags -->
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content={`Today's Crypto News and Breaking Stories · stocknear`}/>
<meta name="twitter:description" content={`Get the latest crypto news and breaking stories from the world's best finance and investing websites.`} />
<!-- Add more Twitter meta tags as needed -->
</svelte:head>
<section class="w-full max-w-5xl overflow-hidden m-auto mt-10">
<div class="flex justify-center w-full m-auto overflow-hidden">
<div class="relative flex justify-center items-center overflow-hidden">
<main>
<div class="w-screen sm:w-full m-auto">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-5">
{#if news.length !== 0}
{#each news as item}
<div class="flex flex-col w-full mt-5 bg-[#27272A] shadow-lg h-auto sm:h-[440px] pb-10 sm:pb-5 rounded-none sm:rounded-lg m-auto">
<a href={item.url} target="_blank">
<div class="h-48 sm:h-60 m-auto border border-slate-800 rounded-none sm:rounded-lg ">
<img src="https://cdn.snapi.dev/images/v1/n/1/press11-2568834.jpg" class="w-screen sm:w-full h-48 sm:h-60 rounded-none sm:rounded-t-lg" alt="news image" loading="lazy">
</div>
</a>
<div class="pl-3 pr-3">
<label class="mt-3 mb-3 cursor-pointer text-xs text-gray-200 flex flex-row items-center">
Source: {item?.site} · {formatDate(item?.date)} ago
</label>
<a href={item?.url} target="_blank" class="text-lg font-bold text-white">
{item?.title?.length > 120 ? item?.title?.slice(0,120) +'...' : item?.title}
</a>
<p class="text-white text-sm mt-2">
{item?.text?.length > 100 ? item?.text?.slice(0,100) + "..." : item?.text}
</p>
</div>
</div>
{/each}
<InfiniteLoading on:infinite={infiniteHandler} />
{/if}
</div>
</main>
</div>
</div>
</section>

View File

@ -1,38 +0,0 @@
import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
const getPressRelease = async () => {
let output;
// Get cached data for the specific tickerID
const cachedData = getCache("", "getPressRelease");
if (cachedData) {
output = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { newsType: "press-releases" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/market-news", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getPressRelease'
setCache("", output, "getPressRelease");
}
return output;
};
// Make sure to return a promise
return {
getPressRelease: await getPressRelease(),
};
};

View File

@ -34,9 +34,9 @@ for(let i = 0; i < notificationList?.length; i++)
if (notificationIdList.length !== 0)
{
const postData = {'unreadList': notificationIdList};
const postData = {'unreadList': notificationIdList, 'path': 'update-notifications'};
await fetch(data?.fastifyURL+'/update-notifications', {
await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -0,0 +1,60 @@
import { getPartyForPoliticians } from "$lib/utils";
let politicianDistrict;
let politicianCongress;
let politicianParty = "n/a";
// Function to load images only when they are viewed
export const load = async ({ locals, params }) => {
const getPolitician = async () => {
let res;
const { apiURL, apiKey } = locals;
const postData = { politicianId: params.slug };
const response = await fetch(apiURL + "/politician-stats", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
const history = output?.history;
// Cache the data for this specific tickerID with a specific name 'getPolitician'
if (output && history?.length > 0) {
let firstItem = history?.at(0);
let representative = firstItem?.representative || "";
representative = representative
?.replace("Jr", "")
?.replace(/Dr./g, "")
?.replace(/Dr_/g, "");
const fullName = representative
?.replace(/(\s(?:Dr\s)?\w(?:\.|(?=\s)))?\s/g, "_")
?.trim();
firstItem.representative = fullName?.replace(/_/g, " ");
const party = getPartyForPoliticians(firstItem?.representative);
firstItem.party = party;
politicianParty = firstItem?.party;
politicianDistrict = firstItem?.district;
politicianCongress = firstItem?.congress;
}
res = { output, politicianParty, politicianDistrict, politicianCongress };
return res;
};
// Make sure to return a promise
return {
getPolitician: await getPolitician(),
};
};

View File

@ -1,69 +0,0 @@
import { getCache, setCache } from "$lib/store";
import defaultAvatar from "$lib/images/senator/default-avatar.png";
import { getPartyForPoliticians } from "$lib/utils";
let politicianDistrict;
let politicianCongress;
let politicianParty = "n/a";
// Function to load images only when they are viewed
export const load = async ({ parent, params }) => {
const getPolitician = async () => {
let res;
// Get cached data for the specific tickerID
const cachedData = getCache(params.slug, "getPolitician");
if (cachedData) {
res = cachedData;
} else {
const { apiURL, apiKey } = await parent();
const postData = { politicianId: params.slug };
const response = await fetch(apiURL + "/politician-stats", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
const history = output?.history;
// Cache the data for this specific tickerID with a specific name 'getPolitician'
if (output && history?.length > 0) {
let firstItem = history?.at(0);
let representative = firstItem?.representative || "";
representative = representative
?.replace("Jr", "")
?.replace(/Dr./g, "")
?.replace(/Dr_/g, "");
const fullName = representative
?.replace(/(\s(?:Dr\s)?\w(?:\.|(?=\s)))?\s/g, "_")
?.trim();
firstItem.representative = fullName?.replace(/_/g, " ");
const party = getPartyForPoliticians(firstItem?.representative);
firstItem.party = party;
politicianParty = firstItem?.party;
politicianDistrict = firstItem?.district;
politicianCongress = firstItem?.congress;
}
res = { output, politicianParty, politicianDistrict, politicianCongress };
setCache(params.slug, res, "getPolitician");
}
return res;
};
// Make sure to return a promise
return {
getPolitician: await getPolitician(),
};
};

View File

@ -1,134 +0,0 @@
import { error, fail, redirect } from "@sveltejs/kit";
import { validateData } from "$lib/utils";
import { loginUserSchema, registerUserSchema } from "$lib/schemas";
export const actions = {
login: async ({ request, locals }) => {
const { formData, errors } = await validateData(
await request.formData(),
loginUserSchema,
);
if (errors) {
return fail(400, {
data: formData,
errors: errors.fieldErrors,
});
}
try {
await locals.pb
.collection("users")
.authWithPassword(formData.email, formData.password);
/*
if (!locals.pb?.authStore?.model?.verified) {
locals.pb.authStore.clear();
return {
notVerified: true,
};
}
*/
} catch (err) {
console.log("Error: ", err);
error(err.status, err.message);
}
redirect(302, "/");
},
register: async ({ locals, request }) => {
const { formData, errors } = await validateData(
await request.formData(),
registerUserSchema,
);
if (errors) {
return fail(400, {
data: formData,
errors: errors.fieldErrors,
});
}
try {
let newUser = await locals.pb.collection("users").create(formData);
/*
await locals.pb?.collection('users').update(
newUser?.id, {
'freeTrial' : true,
'tier': 'Pro', //Give new users a free trial for the Pro Subscription
});
*/
await locals.pb.collection("users").requestVerification(formData.email);
} catch (err) {
console.log("Error: ", err);
error(err.status, err.message);
}
try {
await locals.pb
.collection("users")
.authWithPassword(formData.email, formData.password);
} catch (err) {
console.log("Error: ", err);
error(err.status, err.message);
}
redirect(303, "/");
},
oauth2: async ({ url, locals, request, cookies }) => {
const authMethods = await locals?.pb
?.collection("users")
?.listAuthMethods();
const data = await request?.formData();
const providerSelected = data?.get("provider");
if (!authMethods) {
return {
authProviderRedirect: "",
authProviderState: "",
};
}
const redirectURL = `${url.origin}/oauth`;
const targetItem = authMethods.authProviders?.findIndex(
(item) => item?.name === providerSelected,
);
//console.log("==================")
//console.log(authMethods.authProviders)
//console.log('target item is: ', targetItem)
const provider = authMethods.authProviders[targetItem];
const authProviderRedirect = `${provider.authUrl}${redirectURL}`;
const state = provider.state;
const verifier = provider.codeVerifier;
cookies.set("state", state, {
httpOnly: true,
sameSite: "lax",
secure: true,
path: "/",
maxAge: 60 * 60,
});
cookies.set("verifier", verifier, {
httpOnly: true,
sameSite: "lax",
secure: true,
path: "/",
maxAge: 60 * 60,
});
cookies.set("provider", providerSelected, {
httpOnly: true,
sameSite: "lax",
secure: true,
path: "/",
maxAge: 60 * 60,
});
redirect(302, authProviderRedirect);
},
};

View File

@ -1,878 +0,0 @@
<script lang='ts'>
import { goto } from '$app/navigation';
import { screenWidth, numberOfUnreadNotification} from '$lib/store';
import Searchbar from '$lib/components/Searchbar.svelte';
import AddPortfolio from '$lib/components/AddPortfolio.svelte';
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
export let data;
export let form;
let cloudFrontUrl = import.meta.env.VITE_IMAGE_URL;
let isLoaded = false;
async function getUserPortfolio()
{
const postData = {'userId': data?.user?.id};
const response = await fetch(data?.fastifyURL+'/get-portfolio', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
});
portfolio = (await response.json())?.items;
}
const currentDate = new Date();
const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
const currentMonthIndex = currentDate.getMonth();
let displayMonth = 'n/a';
if (currentMonthIndex >= 0 && currentMonthIndex < monthNames.length) {
displayMonth = monthNames[currentMonthIndex];
}
let portfolio = [];
let lastUpdate = 'n/a';
let holdingsList = [];
let tradingHistoryList = [];
let LoginPopup;
onMount( async () => {
if(data?.user)
{
await getUserPortfolio();
}
else {
LoginPopup = (await import('$lib/components/LoginPopup.svelte')).default;
}
if (portfolio?.length !== 0)
{
holdingsList = portfolio?.at(0)['holdings'] || [];
tradingHistoryList = portfolio?.at(0)['tradingHistory'] || [];
tradingHistoryList.sort((a, b) => new Date(b?.date) - new Date(a?.date));
lastUpdate = portfolio[0]?.updated;
}
isLoaded = true;
})
let displayChange = 'Change in %';
let showTradingHistory = false;
function handleChange(state:string)
{
displayChange = state;
}
let deg = 0;
async function handleUpdatePortfolio() {
deg = (deg + 180) % 360;
await getUserPortfolio();
if (portfolio?.length !== 0)
{
holdingsList = portfolio[0]['holdings'] || [];
tradingHistoryList = portfolio[0]['tradingHistory'] || [];
tradingHistoryList.sort((a, b) => new Date(a?.date) - new Date(b?.date));
lastUpdate = portfolio[0]?.updated;
}
}
let charNumber = 20;
$: {
if($screenWidth < 640) {
charNumber = 20;
}
else {
charNumber = 30
}
}
</script>
<svelte:head>
<title> {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} Portfolio · stocknear</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="description" content="Join our monthly Portfolio Tournament for free to win real prizes.">
<!-- Other meta tags -->
<meta property="og:title" content="Portfolio · stocknear"/>
<meta property="og:description" content="Join our monthly Portfolio Tournament for free to win real prizes.">
<meta property="og:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>
<meta property="og:type" content="website"/>
<!-- Add more Open Graph meta tags as needed -->
<!-- Twitter specific meta tags -->
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content="Portfolio · stocknear"/>
<meta name="twitter:description" content="Join our monthly Portfolio Tournament for free to win real prizes.">
<meta name="twitter:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>
<!-- Add more Twitter meta tags as needed -->
</svelte:head>
<div class="w-full max-w-4xl overflow-hidden m-auto min-h-screen pt-5 mb-40">
<div class="w-full max-w-4xl m-auto sm:bg-[#27272A] sm:rounded-xl h-auto pl-10 pr-10 pt-5 sm:pb-10 sm:pt-10 mt-3 mb-8">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-10">
<!-- Start Column -->
<div>
<div class="flex flex-col justify-center items-center">
<h1 class="text-center text-3xl sm:text-4xl text-white font-bold mb-2">
Portfolio Tournament
</h1>
<h1 class="text-center text-lg sm:text-2xl text-white font-medium mb-5">
{displayMonth} 2024 🚀
</h1>
</div>
<span class="hidden sm:block text-white text-md font-medium text-center flex justify-center items-center ">
🥇Secure the top position in the tournament to claim your fame and prize 🏆
</span>
</div>
<!-- End Column -->
<!-- Start Column -->
<div class="hidden sm:block relative m-auto mb-5 mt-5 sm:mb-0 sm:mt-0">
<svg class="w-40 -my-5" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="glow">
<feGaussianBlur stdDeviation="5" result="glow"/>
<feMerge>
<feMergeNode in="glow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<path fill="#1E40AF" d="M57.6,-58.7C72.7,-42.6,81.5,-21.3,82,0.5C82.5,22.3,74.7,44.6,59.7,60.1C44.6,75.6,22.3,84.3,0,84.3C-22.3,84.2,-44.6,75.5,-61.1,60.1C-77.6,44.6,-88.3,22.3,-87.6,0.7C-86.9,-20.8,-74.7,-41.6,-58.2,-57.7C-41.6,-73.8,-20.8,-85.2,0.2,-85.4C21.3,-85.6,42.6,-74.7,57.6,-58.7Z" transform="translate(100 100)" filter="url(#glow)" />
</svg>
<div class="z-1 absolute top-7">
<img class="w-36 h-fit ml-2" src={cloudFrontUrl+"/assets/portfolio_icon.png"} alt="logo" loading="lazy">
</div>
</div>
<!-- End Column -->
</div>
</div>
{#if isLoaded}
<div class="flex flex-col sm:flex-row justify-center sm:justify-start items-center mt-5">
</div>
{#if portfolio.length === 0}
<div class="bg-[#09090B] pt-5 pb-5 mt-5 sm:rounded-2xl border-t border-b sm:border border-slate-800">
<div class="flex flex-row ">
<div class="text-slate-300 font-bold text-xl pl-5 w-full flex items-center">
Portfolio
</div>
<div class="w-3/4 flex ml-auto mr-3 flex-row justify-end items-center -mt-2">
<a href="/leaderboard" class="text-blue-400 hover:text-white font-medium text-lg mr-3 sm:mr-8 hover:underline">
Rank: 'n/a'
</a>
</div>
</div>
<div class="p-5 grid grid-cols-3 gap-10 flex justify-center items-center mb-2">
<!--Start Column Title-->
<div class="flex flex-col">
<div class="flex flex-row items-center">
<span class="text-white font-bold text-sm sm:text-lg">
Account Value
</span>
<label for="accountValueInfo" class="cursor-pointer">
<svg class="ml-1 w-5 h-5 inline-block" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#2A323C"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>
</label>
</div>
<span class="text-white text-sm sm:text-[1rem] mt-2">
$100,000.00
</span>
</div>
<!--End Column Title-->
<!--Start Column Win Rate-->
<div class="flex flex-col">
<div class="flex flex-row items-center">
<span class="text-white font-bold text-sm sm:text-lg">
Available Cash
</span>
<label for="availableCashInfo" class="cursor-pointer">
<svg class="ml-1 w-5 h-5 inline-block" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#2A323C"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>
</label>
</div>
<span class="text-white text-sm sm:text-[1rem] mt-2">
$100,000.00
</span>
</div>
<!--End Column Win Rate-->
<!--Start Column Performance-->
<div class="flex flex-col">
<div class="flex flex-row items-center">
<span class="text-white font-bold text-sm sm:text-lg">
Overall Return
</span>
<label for="overallReturnInfo" class="cursor-pointer">
<svg class="ml-1 w-5 h-5 inline-block" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#2A323C"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>
</label>
</div>
<span class="text-white text-sm sm:text-[1rem] mt-2">
0.00%
</span>
</div>
</div>
</div>
<div class="flex flex-col justify-center items-center text-gray.400 font-bold text-2xl mt-10">
<label for="{data?.user ? 'addPortfolio' : 'userLogin'}" class="btn btn-md w-64 text-black rounded-lg bg-[#fff] cursor-pointer py-2.5 px-4 btn hover:bg-[#fff]">
Join Tournament
</label>
</div>
{:else}
<div class="bg-[#09090B] border-t border-b sm:border border-slate-800 pt-5 pb-5 mt-5 sm:rounded-2xl">
<div class="flex flex-row items-center">
<div class="text-slate-300 font-bold text-xl pl-5 w-full flex items-center">
Portfolio
<label on:click={handleUpdatePortfolio} class="sm:cursor-pointer rounded-full w-8 h-8 relative sm:hover:bg-gray-800 inline-block ml-2">
<svg class=" {`rotate-${deg}`} transition-transform rounded-full w-5 h-5 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2" fill="#ffffff" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 495.099 495.099" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g id="XMLID_73_"> <g id="XMLID_75_"> <path id="XMLID_76_" d="M203.82,1.475c-1.28,1.472-1.422,3.624-0.328,5.247l28.608,42.45 C129.497,57.098,48.389,142.944,48.389,247.546c0,59.917,26.716,113.6,68.714,150.149c3.524-4.587,7.842-8.597,13.274-11.282 l34.04-16.833c-38.96-26.623-64.605-71.382-64.605-122.034c0-75.819,57.44-138.424,131.083-146.733l-27.394,40.658 c-1.096,1.625-0.962,3.774,0.327,5.249c1.289,1.473,3.405,1.891,5.154,1.028c35.723-17.665,103.131-51.013,135.047-66.803 c5.734-2.837,5.722-11.012-0.013-13.846c-92.208-45.566-75.115-37.037-135.041-66.653C207.225-0.418,205.108,0.001,203.82,1.475z"></path> </g> <path id="XMLID_74_" d="M446.71,247.546c0-59.758-26.582-113.314-68.403-149.856c-3.507,4.494-7.734,8.445-12.957,10.998 l-34.415,17.015c38.817,26.649,64.353,71.309,64.353,121.844c0,75.585-57.089,138.005-130.397,146.634l27.335-40.551 c1.096-1.624,0.954-3.773-0.326-5.248c-1.289-1.473-3.408-1.891-5.156-1.028c-51.816,25.61-17.87,8.829-141.178,69.776 c-1.472,0.727-2.401,2.225-2.401,3.867v0.007c0,1.65,0.929,3.139,2.401,3.858c0,0,95.723,47.322,141.169,69.794 c1.748,0.862,3.867,0.443,5.156-1.028c1.289-1.475,1.423-3.626,0.326-5.249l-28.615-42.475 C365.919,437.667,446.71,351.946,446.71,247.546z"></path> </g> </g></svg>
</label>
</div>
<div class="w-3/4 flex ml-auto mr-3 flex-row justify-end items-center -mt-2">
<a href="/leaderboard" class="text-blue-400 hover:text-white font-medium text-lg mr-3 sm:mr-8 hover:underline">
Rank: {portfolio[0]?.rank === 0 ? 'n/a' : portfolio[0]?.rank }
</a>
</div>
</div>
<div class="p-5 grid grid-cols-3 gap-10 flex justify-center items-center mb-2">
<!--Start Column Title-->
<div class="flex flex-col">
<div class="flex flex-row items-center">
<span class="text-white font-bold text-sm sm:text-lg">
Account Value
</span>
<label for="accountValueInfo" class="cursor-pointer">
<svg class="ml-1 w-5 h-5 inline-block" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#2A323C"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>
</label>
</div>
<span class="text-white text-sm sm:text-[1rem] mt-2">
${new Intl.NumberFormat("en", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(portfolio?.at(0)?.accountValue)}
</span>
</div>
<!--End Column Title-->
<!--Start Column Win Rate-->
<div class="flex flex-col">
<div class="flex flex-row items-center">
<span class="text-white font-bold text-sm sm:text-lg">
Available Cash
</span>
<label for="availableCashInfo" class="cursor-pointer">
<svg class="ml-1 w-5 h-5 inline-block" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#2A323C"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>
</label>
</div>
<span class="text-white text-sm sm:text-[1rem] mt-2">
${new Intl.NumberFormat("en", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(portfolio?.at(0)?.availableCash)}
</span>
</div>
<!--End Column Win Rate-->
<!--Start Column Performance-->
<div class="flex flex-col">
<div class="flex flex-row items-center">
<span class="text-white font-bold text-sm sm:text-lg">
Overall Return
</span>
<label for="overallReturnInfo" class="cursor-pointer">
<svg class="ml-1 w-5 h-5 inline-block" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#2A323C"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#2A323C" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g></svg>
</label>
</div>
{#if portfolio?.at(0)?.overallReturn > 0}
<span class="text-[#37C97D] text-sm sm:text-[1rem] mt-2">
+{portfolio?.at(0)?.overallReturn?.toFixed(2)}%
</span>
{:else if portfolio?.at(0)?.overallReturn < 0}
<span class="text-[#FF2F1F] text-sm sm:text-[1rem] mt-2">
{portfolio?.at(0)?.overallReturn?.toFixed(2)}%
</span>
{:else}
<span class="text-white text-sm sm:text-[1rem] mt-2">
{portfolio?.at(0)?.overallReturn?.toFixed(2)}%
</span>
{/if}
</div>
<!--End Column Performance-->
<!--
<div class="flex flex-col">
<div class="flex flex-row">
<span class="text-white font-bold text-md">
Alpha
</span>
<label for="alphaInfo" class="cursor-pointer">
<svg class="w-5 h-5 inline-block ml-1 mb-1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#27272A000"> <g id="SVGRepo_bgCarrier" stroke-width="0"></g> <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g> <g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g> </svg>
</label>
</div>
<span class="text-white text-sm">
{portfolio?.at(0)?.metrics['alpha']}
</span>
</div>
<div class="flex flex-col">
<div class="flex flex-row">
<span class="text-white font-bold text-md">
Beta
</span>
<label for="betaInfo" class="cursor-pointer">
<svg class="w-5 h-5 inline-block ml-1 mb-1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#27272A000"> <g id="SVGRepo_bgCarrier" stroke-width="0"></g> <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g> <g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g> </svg>
</label>
</div>
<span class="text-white text-sm">
{portfolio[0]?.metrics['beta']}
</span>
</div>
<div class="flex flex-col">
<div class="flex flex-row">
<span class="text-white font-bold text-md">
Max Drawdown
</span>
<label for="maxDrawdownInfo" class="cursor-pointer">
<svg class="w-5 h-5 inline-block ml-1 mb-1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="#27272A000"> <g id="SVGRepo_bgCarrier" stroke-width="0"></g> <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g> <g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="info-circle"> <g> <circle cx="12" cy="12" data-name="--Circle" fill="none" id="_--Circle" r="10" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle> <line fill="none" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="12" y2="16"></line> <line fill="none" stroke="#3276C3" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" x1="12" x2="12" y1="8" y2="8"></line> </g> </g> </g> </g> </svg>
</label>
</div>
{#if portfolio[0]?.metrics['maxDrawdown'] < 0}
<span class="text-[#FF2F1F] text-sm">
{portfolio[0]?.metrics['maxDrawdown']} %
</span>
{:else}
<span class="text-white text-sm">
{portfolio[0]?.metrics['maxDrawdown']} %
</span>
{/if}
</div>
-->
</div>
</div>
{/if}
{#if portfolio?.length !== 0}
<div class="bg-[#09090B] pt-5 pb-5 mt-10 sm:rounded-2xl">
<div class="flex flex-row justify-between items-center">
<div class="text-slate-300 font-bold text-lg pl-5">
Holdings
</div>
<label for="holdingsCategoryInfo" class="cursor-pointer hover:underline text-blue-400 font-medium text-sm mr-3">
{displayChange}
<svg class="w-5 h-5 inline-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(180 12 12)"><path fill="#1a56db" d="m12 10.8l-4.6 4.6L6 14l6-6l6 6l-1.4 1.4l-4.6-4.6Z"/></g></svg>
</label>
</div>
{#if holdingsList?.length === 0}
<div class="flex justify-center items-center text-white mt-5 sm:mt-0 font-medium text-md">
No Holdings found
</div>
<div class="w-full text-start flex flex-col justify-center items-center mt-2 sm:mt-5">
<Searchbar />
<span class="text-sm text-white m-auto mt-3 w-3/4 sm:w-1/2 text-center">
Click on the Search Icon to find your favorite stocks and begin growing your portfolio.
</span>
</div>
{:else}
<table class="table table-sm table-compact w-full m-auto mt-4">
<tbody class="">
{#each holdingsList as item, index}
<tr on:click={() => goto(`/${item?.assetType === 'etf' ? 'etf' : 'stocks'}/${item?.symbol}`)} class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] {index % 2 === 0 ? 'bg-opacity-[0.25] bg-[#323239]' : 'bg-[#09090B]'} border-b-[#09090B] cursor-pointer">
<td class="text-white border-b border-[#27272A]">
<div class="flex flex-row">
<div class="flex flex-col text-sm">
<span class="text-blue-400">{item?.symbol}</span>
<span class="text-white">{item.name?.length > charNumber ? item?.name?.slice(0,charNumber) + "..." : item?.name}</span>
<div class="flex flex-row items-center mt-0.5">
<span class="bg-[#333333] rounded text-white pl-1 pr-1 w-fit">
x{item?.numberOfShares}
</span>
<span class="text-white ml-1">
${new Intl.NumberFormat("en", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
}).format(item?.numberOfShares * item?.currentPrice)}
</span>
</div>
</div>
</div>
</td>
<td class="text-white border-b border-[#27272A]">
<div class="flex flex-row justify-end items-center">
<div class="flex flex-col mt-3">
<span class="text-white text-md ml-auto">${item?.currentPrice?.toFixed(2)}</span>
<div class="flex flex-row mt-1">
{#if displayChange === "Change in %"}
{#if item?.sinceBoughtChange >=0}
<svg class="w-5 h-5 -mr-0.5 -mt-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#37C97D" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#37C97D] text-xs font-medium">+{item?.sinceBoughtChange?.toFixed(2)}%</span>
{:else}
<svg class="w-5 h-5 -mr-0.5 -mt-0.5 rotate-180" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#FF2F1F" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#FF2F1F] text-xs font-medium">{item?.sinceBoughtChange?.toFixed(2)}%</span>
{/if}
{:else}
{#if item?.sinceBoughtChange >=0}
<svg class="w-5 h-5 -mr-0.5 -mt-0.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#37C97D" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#37C97D] text-xs font-medium">+${(item?.numberOfShares * item?.currentPrice * item?.sinceBoughtChange/100 )?.toFixed(2)}</span>
{:else}
<svg class="w-5 h-5 -mr-0.5 -mt-0.5 rotate-180" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g id="evaArrowUpFill0"><g id="evaArrowUpFill1"><path id="evaArrowUpFill2" fill="#FF2F1F" d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"/></g></g></svg>
<span class="text-[#FF2F1F] text-xs font-medium">${(item?.numberOfShares * item?.currentPrice * item?.sinceBoughtChange/100 )?.toFixed(2)}</span>
{/if}
{/if}
</div>
</div>
</div>
</td>
{/each}
</tbody>
</table>
{/if}
</div>
<div class="bg-[#09090B] pt-5 pb-5 mt-10 sm:rounded-2xl {!showTradingHistory ? 'shadow-md' : ''}">
<div on:click={() => showTradingHistory = !showTradingHistory} class="cursor-pointer flex flex-row items-center justify-between">
<div class="text-slate-300 font-bold text-lg pl-5 w-full">
Trading History
</div>
<div class="pr-5 button text-md mt-1 cursor-pointer text-white font-medium hover:text-[#DF4390] hover:underline">
<svg class="{showTradingHistory ? 'rotate-180' : 'rotate-0'} transform transition-transform w-7 h-7 inline-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g transform="rotate(180 12 12)"><path fill="#1a56db" d="m12 10.8l-4.6 4.6L6 14l6-6l6 6l-1.4 1.4l-4.6-4.6Z"/></g></svg>
</div>
</div>
{#if tradingHistoryList?.length === 0}
<div class="flex justify-center items-center text-slate-300 font-medium text-md ">
{#if showTradingHistory}
<span class="text-slate-200 text-md mt-5">
No Trading History found
</span>
{/if}
</div>
{:else}
{#if showTradingHistory}
<div transition:fade={{ delay: 0, duration: 80 }} in={showTradingHistory}>
<table class="table table-sm table-compact w-full m-auto mt-4">
<tbody class="">
{#each tradingHistoryList as item, index}
<tr class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] {index % 2 === 0 ? 'bg-opacity-[0.25] bg-[#323239]' : 'bg-[#09090B]'} border-b-[#09090B] cursor-pointer">
<td class="text-white border-b border-[#27272A]">
<div class="flex flex-row items-center">
<div class="flex flex-col">
<span class="text-blue-400">{item?.symbol}</span>
<span class="text-white text-xs sm:text-sm">{item.name?.length > charNumber ? item?.name.slice(0,charNumber) + "..." : item?.name}</span>
</div>
<div class="ml-auto text-gray-300 mt-1 text-sm">
{#if item?.type === 'buy'}
<span class="text-[#37C97D]">Bought</span> {item?.numberOfShares} shares at {item?.price?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
{:else}
<span class="text-[#FF2F1F]">Sold</span> {item?.numberOfShares} shares at {item?.price?.toLocaleString(undefined, {
style: 'currency',
currency: 'USD',
})}
{/if}
</div>
</div>
</td>
{/each}
</tbody>
</table>
</div>
{/if}
{/if}
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<span class="loading loading-spinner loading-md"></span>
</label>
</div>
</div>
{/if}
</div>
<!--Start Login Modal-->
{#if LoginPopup}
<LoginPopup form={form}/>
{/if}
<!--End Login Modal-->
<!--Start Account Value Modal-->
<input type="checkbox" id="accountValueInfo" class="modal-toggle" />
<dialog id="accountValueInfo" class="modal modal-bottom sm:modal-middle ">
<label id="accountValueInfo" for="accountValueInfo" class="cursor-pointer modal-backdrop bg-[#000] bg-opacity-[0.5]"></label>
<div class="modal-box w-full bg-[#09090B]">
<div class="text-white mb-5">
<h3 class="font-bold text-2xl mb-5">
Account Value
</h3>
Account value in a portfolio refers to the total worth of the investments it holds.
</div>
</div>
</dialog>
<!--End Account Value Modal-->
<!--Start Available Cash Modal-->
<input type="checkbox" id="availableCashInfo" class="modal-toggle" />
<dialog id="availableCashInfo" class="modal modal-bottom sm:modal-middle ">
<label id="availableCashInfo" for="availableCashInfo" class="cursor-pointer modal-backdrop bg-[#000] bg-opacity-[0.5]"></label>
<div class="modal-box w-full bg-[#09090B]">
<div class="text-white mb-5">
<h3 class="font-bold text-2xl mb-5">
Available Cash
</h3>
Available cash in a portfolio refers to the amount of money that is accessible for new investments, excluding the value of existing holdings.
</div>
</div>
</dialog>
<!--End Available Cash <Modal-->
<!--Start Overall Return Modal-->
<input type="checkbox" id="overallReturnInfo" class="modal-toggle" />
<dialog id="overallReturnInfo" class="modal modal-bottom sm:modal-middle ">
<label id="overallReturnInfo" for="overallReturnInfo" class="cursor-pointer modal-backdrop bg-[#000] bg-opacity-[0.5]"></label>
<div class="modal-box w-full bg-[#09090B]">
<div class="text-white mb-5">
<h3 class="font-bold text-2xl mb-5">
Overall Return
</h3>
Overall return in a portfolio is the total gain or loss on all investments as a percentage of the initial investment.
</div>
</div>
</dialog>
<!--End Overall Return Modal-->
<!--Start Alpha Modal-->
<input type="checkbox" id="alphaInfo" class="modal-toggle" />
<dialog id="alphaInfo" class="modal modal-bottom sm:modal-middle ">
<label id="alphaInfo" for="alphaInfo" class="cursor-pointer modal-backdrop"></label>
<div class="modal-box w-full bg-[#09090B] ">
<label for="alphaInfo" class="btn btn-sm btn-circle absolute right-2 top-2 bg-red-600 hover:bg-red-800"></label>
<div class="text-white">
<h3 class="font-bold text-2xl mb-5">
Alpha
</h3>
Alpha is a measure of a portfolio's performance relative to the S&P500 benchmark. A positive alpha indicates it outperformed the index, while a negative alpha means it underperformed.
</div>
<div class="modal-action">
<!-- if there is a button in form, it will close the modal -->
<label for="alphaInfo" class="btn bg-red-600 text-white">Got it</label>
</div>
</div>
</dialog>
<!--End Alpha Modal-->
<!--Start Beta Modal-->
<input type="checkbox" id="betaInfo" class="modal-toggle" />
<dialog id="betaInfo" class="modal modal-bottom sm:modal-middle ">
<label id="betaInfo" for="betaInfo" class="cursor-pointer modal-backdrop"></label>
<div class="modal-box w-full bg-[#09090B] ">
<label for="betaInfo" class="btn btn-sm btn-circle absolute right-2 top-2 bg-red-600 hover:bg-red-800"></label>
<div class="text-white">
<h3 class="font-bold text-2xl mb-5">
Beta
</h3>
Beta measures your portfolio's sensitivity to market movements.
<br>
<br>
A beta of 1 means it moves in line with the S&P500. A beta less than 1 implies lower volatility, while a beta greater than 1 suggests higher volatility.
</div>
<div class="modal-action">
<!-- if there is a button in form, it will close the modal -->
<label for="betaInfo" class="btn bg-red-600 text-white">Got it</label>
</div>
</div>
</dialog>
<!--End Beta Modal-->
<!--Start Max Drawdown Modal-->
<input type="checkbox" id="maxDrawdownInfo" class="modal-toggle" />
<dialog id="maxDrawdownInfo" class="modal modal-bottom sm:modal-middle ">
<label id="maxDrawdownInfo" for="maxDrawdownInfo" class="cursor-pointer modal-backdrop bg-[#fff] bg-opacity-[0.08]"></label>
<div class="modal-box w-full bg-[#09090B] border border-slate-800">
<label for="maxDrawdownInfo" class="btn btn-sm btn-circle absolute right-2 top-2 bg-red-600 hover:bg-red-800"></label>
<div class="text-white">
<h3 class="font-bold text-2xl mb-5">
Max Drawdown
</h3>
Max Drawdown measures the largest loss from a portfolio peak to its lowest point, showing the worst possible drop in value.
</div>
<div class="modal-action">
<!-- if there is a button in form, it will close the modal -->
<label for="maxDrawdownInfo" class="btn bg-red-600 text-white">Got it</label>
</div>
</div>
</dialog>
<!--End Max Drawdown Modal-->
<!--Start Holdings Category Modal-->
<input type="checkbox" id="holdingsCategoryInfo" class="modal-toggle" />
<dialog id="holdingsCategoryInfo" class="modal modal-bottom sm:modal-middle ">
<label id="holdingsCategoryInfo" for="holdingsCategoryInfo" class="cursor-pointer modal-backdrop bg-[#000] bg-opacity-[0.5]"></label>
<div class="modal-box w-full bg-[#09090B] sm:border sm:border-slate-800">
<div class="text-white">
<h3 class="font-medium text-lg sm:text-2xl mb-10">
Performance
</h3>
<div class="flex flex-col items-center w-full max-w-3xl bg-[#09090B]">
<label for="holdingsCategoryInfo" on:click={() => handleChange('Change in %')} class="cursor-pointer w-full flex flex-row justify-start items-center mb-5">
<div class="flex flex-row items-center w-full bg-[#303030] p-3 rounded-lg {displayChange === 'Change in %' ? 'ring-2 ring-[#04E000]' : ''}">
<span class="ml-1 text-white font-medium mr-auto">
Change in %
</span>
<div class="rounded-full w-8 h-8 relative border border-[#737373]">
{#if displayChange === 'Change in %'}
<svg class="w-full h-full rounded-full" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#27272A000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> <title>ic_fluent_checkmark_circle_48_filled</title> <desc>Created with Sketch.</desc> <g id="🔍-Product-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="ic_fluent_checkmark_circle_48_filled" fill="#04E000" fill-rule="nonzero"> <path d="M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M32.6338835,17.6161165 C32.1782718,17.1605048 31.4584514,17.1301307 30.9676119,17.5249942 L30.8661165,17.6161165 L20.75,27.732233 L17.1338835,24.1161165 C16.6457281,23.6279612 15.8542719,23.6279612 15.3661165,24.1161165 C14.9105048,24.5717282 14.8801307,25.2915486 15.2749942,25.7823881 L15.3661165,25.8838835 L19.8661165,30.3838835 C20.3217282,30.8394952 21.0415486,30.8698693 21.5323881,30.4750058 L21.6338835,30.3838835 L32.6338835,19.3838835 C33.1220388,18.8957281 33.1220388,18.1042719 32.6338835,17.6161165 Z" id="🎨-Color"> </path> </g> </g> </g></svg>
{/if}
</div>
</div>
</label>
<label for="holdingsCategoryInfo" on:click={() => handleChange('Change in $')} class="cursor-pointer w-full flex flex-row justify-start items-center mb-5">
<div class="flex flex-row items-center w-full bg-[#303030] p-3 rounded-lg {displayChange === 'Change in $' ? 'ring-2 ring-[#04E000]' : ''}">
<span class="ml-1 text-white font-medium mr-auto">
Change in $
</span>
<div class="rounded-full w-8 h-8 relative border border-[#737373]">
{#if displayChange === 'Change in $'}
<svg class="w-full h-full rounded-full" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#27272A000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> <title>ic_fluent_checkmark_circle_48_filled</title> <desc>Created with Sketch.</desc> <g id="🔍-Product-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="ic_fluent_checkmark_circle_48_filled" fill="#04E000" fill-rule="nonzero"> <path d="M24,4 C35.045695,4 44,12.954305 44,24 C44,35.045695 35.045695,44 24,44 C12.954305,44 4,35.045695 4,24 C4,12.954305 12.954305,4 24,4 Z M32.6338835,17.6161165 C32.1782718,17.1605048 31.4584514,17.1301307 30.9676119,17.5249942 L30.8661165,17.6161165 L20.75,27.732233 L17.1338835,24.1161165 C16.6457281,23.6279612 15.8542719,23.6279612 15.3661165,24.1161165 C14.9105048,24.5717282 14.8801307,25.2915486 15.2749942,25.7823881 L15.3661165,25.8838835 L19.8661165,30.3838835 C20.3217282,30.8394952 21.0415486,30.8698693 21.5323881,30.4750058 L21.6338835,30.3838835 L32.6338835,19.3838835 C33.1220388,18.8957281 33.1220388,18.1042719 32.6338835,17.6161165 Z" id="🎨-Color"> </path> </g> </g> </g></svg>
{/if}
</div>
</div>
</label>
</div>
</div>
</div>
</dialog>
<!--End Holdings Category Modal-->
<!--Start Add Portfolio Modal-->
<AddPortfolio data={data} />
<!--End Add Portfolio Modal-->

View File

@ -128,10 +128,11 @@ async function handleDelete() {
priceAlertList = [...priceAlertList];
const postData = {
'priceAlertIdList': deletePriceAlertList
'priceAlertIdList': deletePriceAlertList,
'path': 'delete-price-alert'
}
const response = await fetch(data?.fastifyURL+'/delete-price-alert', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -9,7 +9,7 @@ const pages = [
{ title: "/crypto" },
{ title: "/etf/etf-providers" },
{ title: "/etf/new-launches" },
{ title: "/trending" },
//{ title: "/trending" },
{ title: "/price-alert" },
{ title: "/heatmaps" },
{ title: "/donation" },

View File

@ -255,9 +255,9 @@ async function handleCreateStrategy() {
async function handleDeleteStrategy() {
const postData = {'strategyId': selectedStrategy};
const postData = {'strategyId': selectedStrategy, 'path': 'delete-strategy'};
const response = await fetch(data?.fastifyURL+'/delete-strategy', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -325,10 +325,9 @@ async function createStrategy(event)
for (const [key, value] of formData.entries()) {
postData[key] = value;
}
postData['path'] = 'create-strategy';
const response = await fetch(data?.fastifyURL+'/create-strategy', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
@ -576,9 +575,9 @@ async function handleSave(printToast) {
{
strategyList.find(item => item.id === selectedStrategy).rules = ruleOfList;
const postData = {'strategyId': selectedStrategy, 'rules': ruleOfList}
const postData = {'strategyId': selectedStrategy, 'rules': ruleOfList, 'path': 'save-strategy'}
const response = await fetch(data?.fastifyURL+'/save-strategy', {
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"

View File

@ -64,9 +64,9 @@ const getStockScreenerData = async (rules, apiKey, apiURL) => {
};
onmessage = async (event) => {
const { ruleOfList, apiKey, apiURL } = event.data || {};
const { ruleOfList } = event.data || {};
const output = await getStockScreenerData(ruleOfList, apiKey, apiURL);
const output = await getStockScreenerData(ruleOfList);
const stockScreenerData = output?.filter((item) =>
Object?.values(item)?.every(

View File

@ -112,17 +112,20 @@ function handleTypeOfTrade(state:string)
async function toggleUserWatchlist(watchListId: string) {
try {
const watchlistIndex = userWatchList?.findIndex((item) => item?.id === watchListId);
const postData = {
userId: data?.user?.id,
watchListId,
ticker: $stockTicker,
};
const postData = {
'userId': data?.user?.id,
'watchListId': watchListId,
'ticker': $stockTicker,
'path': 'update-watchlist'
};
const response = await fetch(data?.fastifyURL+'/update-watchlist', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postData),
});
const response = await fetch('/api/fastify-post-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData),
});
if (!response.ok) {
throw new Error("Network response was not ok");
@ -144,23 +147,7 @@ function handleTypeOfTrade(state:string)
}
}
/*
async function fetchPortfolio()
{
const postData = {'userId': data?.user?.id};
const response = await fetch(data?.fastifyURL+'/get-portfolio-data', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(postData)
});
userPortfolio = (await response.json())?.items;
}
*/
function sendMessage(message) {
if (socket && socket.readyState === WebSocket.OPEN) {
@ -276,33 +263,7 @@ async function fetchPortfolio()
$: isTickerIncluded = userWatchList?.some((item) => item.user === data?.user?.id && item.ticker?.includes($stockTicker));
/*
$: {
if(userPortfolio) {
availableCash = userPortfolio?.at(0)?.availableCash;
const userHoldingList = userPortfolio?.at(0)?.holdings || [];
const stockIndex = userHoldingList?.findIndex(stock => stock?.symbol === $stockTicker);
if (stockIndex !== -1)
{
holdingShares = userHoldingList[stockIndex]['numberOfShares'];
}
else {
holdingShares = 0;
}
}
}
$: {
if(typeof window !== 'undefined' && $traded && data?.user && $stockTicker?.length !== 0)
{
fetchPortfolio();
$traded = false;
}
}
*/
$: charNumber = $screenWidth < 640 ? 12 : 25;

View File

@ -62,12 +62,12 @@ function getBarChart(data, dates) {
onmessage = async (event: MessageEvent) => {
const data = event.data?.message;
const rawData = data?.sort(
(a, b) => new Date(b?.transactionDate) - new Date(a?.transactionDate),
(a, b) => new Date(b?.transactionDate) - new Date(a?.transactionDate)
);
const latestDate = findLatestDateInPast(rawData);
let historicalPrice = event.data?.historicalPrice?.filter(
(item) => new Date(item?.time) >= new Date(latestDate),
(item) => new Date(item?.time) >= new Date(latestDate)
);
const dataPoints = historicalPrice?.map(({ close }) => close);
@ -76,9 +76,6 @@ onmessage = async (event: MessageEvent) => {
let finalData = { rawData, dataPoints, dates, barChartData };
postMessage({ message: "success", finalData });
// Sending data back to the main thread
//postMessage({ message: 'Data received in the worker', ticker, apiURL });
};
export {};

View File

@ -1,216 +0,0 @@
<script lang='ts'>
import { numberOfUnreadNotification } from '$lib/store';
let cloudFrontUrl = import.meta.env.VITE_IMAGE_URL;
</script>
<svelte:head>
<title> {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ''} Portfolio Tournament Rules · stocknear</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="description" content="Tournament Rules for our monthyl Portfolio competition.">
<!-- Other meta tags -->
<meta property="og:title" content="Portfolio Tournament Rules · stocknear"/>
<meta property="og:description" content="Tournament Rules for our monthyl Portfolio competition.">
<meta property="og:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>
<meta property="og:type" content="website"/>
<!-- Add more Open Graph meta tags as needed -->
<!-- Twitter specific meta tags -->
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content="Portfolio Tournament Rules · stocknear"/>
<meta name="twitter:description" content="Tournament Rules for our monthyl Portfolio competition.">
<meta name="twitter:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>
<!-- Add more Twitter meta tags as needed -->
</svelte:head>
<div class="w-full max-w-4xl overflow-hidden m-auto mt-10 min-h-screen pt-10 mb-40">
<div class="w-full sm:rounded-xl bg-[#09090B] h-auto sm:p-10 mt-3">
<div class="text-white text-4xl sm:text-5xl text-center p-3 mt-10 sm:mt-5">
<h1 class="font-bold ">Announcement and Rules</h1>
</div>
<div class="text-white text-lg mt-3 text-center pl-10 pr-10">
I am thrilled to announce our latest stocknear exclusive Portfolio Tournament! This exciting event offers you the incredible opportunity to win fantastic prizes each and every month. Join us now and experience the thrill of competing for one or more prizes on a regular basis!
<!-- Start Image -->
<div class="m-auto flex justify-center mt-3">
<div class="relative">
<div class="z-1 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
<img class="w-28 mr-4" src={cloudFrontUrl+"/assets/announcement_logo.png"} alt="logo" loading="lazy">
</div>
<svg id="visual" viewBox="0 0 960 540" width="380" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<filter id="glow">
<feGaussianBlur stdDeviation="12" result="glow"/>
<feMerge>
<feMergeNode in="glow"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<g transform="translate(506.6556351091302 253.49315711627338)">
<path d="M141.5 -60.2C185.5 -5.4 224.8 73.5 200.5 122.1C176.1 170.7 88 188.8 -4.7 191.5C-97.4 194.3 -194.9 181.5 -236.6 122.9C-278.3 64.3 -264.3 -40.1 -215.6 -97.5C-166.9 -155 -83.4 -165.5 -17.4 -155.5C48.7 -145.5 97.4 -114.9 141.5 -60.2" fill="#1E40AF" stroke-width="10" filter="url(#glow)"></path>
</g>
</svg>
</div>
</div>
<!-- End Image -->
</div>
</div>
<div class="flex flex-col bg-[#09090B] w-full sm:rounded-xl h-auto sm:p-10 mt-10">
<h2 class="text-white text-4xl sm:text-5xl text-center font-medium mt-10 sm:mt-5">
Winning Prizes
</h2>
<div class="text-white text-md mt-5 text-center pl-10 pr-10">
Each month, participants stand a chance to win amazing prizes, including 3x Discounts on the Pro Subscription Plan. The winners will be chosen on the first day of the following month. Don't miss out on this fantastic opportunity to be a winner!
</div>
<!--
<img class="w-11/12 m-auto mt-10 rounded-lg" src={cloudFrontUrl+"/assets/tournament_logo.jpg"} alt="Logo" loading="lazy"/>
-->
<span class="text-white text-md mt-10 p-3 text-center ">
The Top 3 participant in the <a href="/leaderboard" class="text-blue-500 hover:text-white">
Leaderboard</a>
will be rewarded accordingly with custom badges in their profile.
</span>
</div>
<div class="flex flex-col bg-[#09090B] w-full sm:rounded-xl h-auto sm:p-10 mt-10">
<h3 class="text-white text-4xl sm:text-5xl font-medium text-center m-auto mt-10 sm:mt-5">
Rules
</h3>
<span class="text-white text-md mt-8 text-center sm:text-start pl-10 pr-10">
Participating in the tournament is simple, and we've outlined the rules for you below:
</span>
<ol class="text-white list-disc ml-5 sm:ml-3 p-3">
<li class="p-1">
To join, you must create a User Account and be at least 18 years old. Don't forget to accept our Terms and Conditions to complete your participation.
</li>
<li class="p-1">
One account per participant is allowed. Any form of cheating, including the use of multiple accounts by the same person, is strictly prohibited.
</li>
<li class="p-1">
You'll begin with an initial in-game Budget of $100,000.00 to kickstart your investment journey.
</li>
<li class="p-1">
Utilize your trading skills to buy and sell stocks strategically, aiming to maximize your Return on Investment.
</li>
<li class="p-1">
The ranking system is solely based on your Overall Return, so make every move count.
</li>
<li class="p-1">
Once the tournament concludes, the Top 3 participants with the highest Overall Return will each be awarded a prize for their outstanding performance.
</li>
</ol>
<span class="text-white text-md mt-4 text-center font-bold mb-5 ">
Now that you know the rules, gear up for an exciting investment challenge! Good luck!
</span>
</div>
<div class="bg-[#09090B] w-full sm:rounded-xl h-auto sm:p-10 mt-10">
<h4 class="text-white text-4xl text-center font-medium mb-3 mt-10 sm:mt-3 p-3">
Terms and Conditions of Participation
</h4>
<ol class="text-white list-decimal ml-6 sm:ml-3 p-2">
<li class="p-1">The organizer of the Prize competition is stocknear.</li>
<li class="p-1">Participation is free of charge.</li>
<li class="p-1">
All individuals worldwide who have reached the age of 18 and comply with the laws and regulations of their respective countries are eligible to participate. Persons involved in the conception and implementation of this prize competition are excluded - this explicitly does not apply to voluntary moderators or other voluntary members who have a special status on the platform.
</li>
<li class="p-1">
The prerequisite for participation in the competition is the creation of a portfolio whereby the user can buy and sell shares. Each user starts with a play money of 100,000 $. To be considered in the final draw and eligible for any prizes, participants must make at least one trade (buy or sell shares) during the tournament period.
</li>
<li class="p-1">
The prize competition will take place automated once a month for an indefinite period of time. The potential winners are drawn automatically, randomly on the first day of each month, whereby the chance of winning depends on the overall return of the portfolio. If a participant has collected too little return of the portfolio, the chance can also be 0 percent.
</li>
<li class="p-1">
The prizes to be awarded may change from month to month.
</li>
<li class="p-1">
After the draw, the accounts of the winners will be checked for possible fraud attempts. If no abnormalities are found, the potential winners will be notified by email from stocknear.
If any abnormalities are found, the draw will be repeated. The organizer reserves the right to refuse and reclaim prizes even after the fact and to permanently exclude participants from competitions in case of suspicion of attempted fraud or manipulation for their own benefit or the benefit of third parties as well as to temporarily or permanently block their accounts.
</li>
<li class="p-1">
The potential winner must respond to the respective prize notification within 14 days,
which will be sent via email, in order to claim the prize.
Only a response via email will be considered.
Otherwise, unclaimed prizes will be included in the next monthly draw.
</li>
<li class="p-1">
The organizer points out that the availability and functionality of the prize draw cannot be guaranteed.
The organizer is not responsible for entries not being included due to technical failures or other reasons.
The prize competition may be terminated or removed due to external circumstances and constraints
without any claims arising for the participants against the organizer.
</li>
<li class="p-1">
The organizer reserves the right to change, discontinue, or suspend the game
and the draw in whole or in part without prior notice in the event
of unforeseen circumstances. These circumstances include, but are not limited to,
the appearance of a computer virus, a program error, unauthorized intervention by third parties,
or mechanical or technical problems beyond the control and influence of the organizer.
</li>
<li class="p-1">
The organizer reserves the right to terminate the prize draw at any time without prior notice.
In this case, any outstanding prizes will be properly awarded.
</li>
<li class="p-1">
Should individual provisions of these terms and conditions of participation be or become invalid,
inadmissible, or unenforceable, this shall not affect the validity of the remaining terms
and conditions. In place of the invalid, inadmissible, or unenforceable clause, provisions
shall be deemed to have been agreed upon that come as close as possible to the economic objectives.
</li>
<li class="p-1">
Furthermore, the <a href="/imprint" class="text-blue-400 hover:text-white">Imprint</a>, <a href="/terms-of-use" class="text-blue-400 hover:text-white">
Terms of Use
</a> and our
<a href="/privacy-policy" class="text-blue-400 hover:text-white">
Privacy Policy
</a>
apply.
The applicable law and jurisdiction for this prize draw shall be determined based on the participant's
country of residence. In case of any legal disputes, the competent courts of the participant's
country of residence shall have exclusive jurisdiction.
</li>
<li class="p-1">
The right of recourse to the courts is excluded.
</li>
</ol>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More