optimize for seo

This commit is contained in:
MuslemRahimi 2025-01-11 11:31:23 +01:00
parent da8a443ee5
commit cbefa701ce
16 changed files with 877 additions and 1561 deletions

View File

@ -6,7 +6,7 @@
// Use the correct reactive declaration // Use the correct reactive declaration
$: { $: {
if ($stockTicker && typeof window !== "undefined") { if ($stockTicker) {
// Correctly check if score is neither undefined nor null // Correctly check if score is neither undefined nor null
$scoreComponent = score !== undefined && score !== null && score !== 0; $scoreComponent = score !== undefined && score !== null && score !== 0;
} }

View File

@ -1,170 +0,0 @@
<script lang="ts">
import { stockTicker, displayCompanyName } from "$lib/store";
import InfoModal from "$lib/components/InfoModal.svelte";
import { formatDate } from "$lib/utils";
export let data;
let rawData = {};
function latestInfoDate(inputDate) {
// Convert the input date string to milliseconds since epoch
const inputDateMs = new Date(inputDate);
// Get today's date in milliseconds since epoch
const todayMs = Date?.now();
// Calculate the difference in milliseconds
const differenceInMs = todayMs - inputDateMs;
// Convert milliseconds to days
const differenceInDays = Math?.floor(
differenceInMs / (1000 * 60 * 60 * 24),
);
// Return the difference in days
return differenceInDays <= 1;
}
function handleMode(i) {
activeIdx = i;
}
const tabs = [
{
title: "Bull Case",
},
{
title: "Bear Case",
},
];
let activeIdx = 0;
$: {
if ($stockTicker && typeof window !== "undefined") {
rawData = data?.getBullBearSay;
activeIdx = 0;
}
}
</script>
{#if Object?.keys(rawData)?.length !== 0}
<div class="space-y-3 overflow-hidden">
<!--Start Content-->
<div class="w-auto lg:w-full p-1 flex flex-col m-auto">
<div class="flex flex-col items-center w-full mb-1">
<div class="flex flex-row justify-start mr-auto items-center">
<!--<img class="h-10 inline-block mr-2" src={copilotIcon} />-->
<div class="flex flex-row items-center">
<label
for="bullBearCase"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-2xl font-bold"
>
Bull Case vs Bear Case
</label>
<InfoModal
title={"Bull Case vs Bear Case"}
content={`Before investing, examine both perspectives. We offer brief analyst report summaries, highlighting both positive ("Bulls Say") and negative ("Bears Say") viewpoints on ${$displayCompanyName}`}
id={"bullBearCase"}
/>
</div>
</div>
</div>
<!--Start Header-->
<div
class="inline-flex justify-center sm:justify-start w-full rounded-md sm:w-auto"
>
<div
class="bg-secondary w-full min-w-24 sm:w-fit relative flex flex-wrap items-center justify-center rounded-md p-1 mt-4"
>
{#each tabs as item, i}
<button
on:click={() => handleMode(i)}
class="group relative z-[1] rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
i
? 'z-0'
: ''} "
>
{#if activeIdx === i}
<div class="absolute inset-0 rounded-md bg-[#fff]"></div>
{/if}
<span
class="relative text-sm block font-semibold {activeIdx === i
? 'text-black'
: 'text-white'}"
>
{item.title}
</span>
</button>
{/each}
</div>
</div>
<!--End Header-->
<span class="text-white text-sm italic mt-6 ml-auto">
<label
class="{latestInfoDate(rawData?.date)
? ''
: 'hidden'} text-black bg-[#fff] mr-2 font-semibold not-italic text-xs rounded px-2 py-0.5"
>New</label
>
Updated {formatDate(rawData?.date)}
</span>
<div class="flex mt-5 h-auto">
<div
class="{activeIdx === 0
? 'bg-[#00FC50]'
: 'bg-[#FF2F1F]'} w-full max-w-[3px] rounded-l-xl"
/>
<span class="text-gray-100 ml-3 text-[1rem] w-full">
{#if activeIdx === 0 && data?.user?.tier === "Pro"}
<p class="pr-1">{rawData?.bullSays}</p>
{:else if activeIdx === 1 && data?.user?.tier === "Pro"}
<p class="pr-1">{rawData?.bearSays}</p>
{:else if activeIdx === 0 && data?.user?.tier !== "Pro"}
<p>
{rawData?.bullSays?.slice(0, 150) + "..."}
<span class="mt-3">
Unlock content with
<a
class="inline-block ml-0.5 text-blue-400 sm:hover:text-white"
href="/pricing"
>Pro Subscription <svg
class="w-4 h-4 mb-1 inline-block text[#A3A3A3] sm:hover:text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/></svg
></a
>
</span>
</p>
{:else if activeIdx === 1 && data?.user?.tier !== "Pro"}
<p>
{rawData?.bearSays?.slice(0, 150) + "..."}
<span class="mt-3">
Unlock content with
<a
class="inline-block ml-0.5 text-blue-400 sm:hover:text-white"
href="/pricing"
>Pro Subscription <svg
class="w-4 h-4 mb-1 inline-block text[#A3A3A3] sm:hover:text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/></svg
></a
>
</span>
</p>
{/if}
</span>
</div>
</div>
</div>
{/if}

View File

@ -1,366 +0,0 @@
<script lang 'ts'></script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="clinicalTrialInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Clinical Trials
</label>
<InfoModal
title={"Clinical Trial"}
content={"Info description coming soon! But first a word of our sponsor skillshare... just kidding"}
id={"clinicalTrialInfo"}
/>
</div>
{#if data?.user?.tier === "Pro"}
{#if isLoaded}
{#if rawData?.length >= 5}
<div class="w-full flex flex-col items-start">
<div class="text-white text-sm sm:text-[1rem] mt-2 mb-2 w-full">
The Biotech company {$displayCompanyName} has conducted a total of
{abbreviateNumber(rawData?.length)} clinical trials. Out of these,
{numOfActive === 0 ? "none" : numOfActive} are currently active,
{numOfCompleted} have been completed, {numOfTerminated} were terminated
and {numOfResults} trials have produced results.
</div>
</div>
<div class="pb-2 rounded-md bg-default">
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
</div>
<!--
<h2 class="mt-10 mr-1 flex flex-row items-center text-white text-xl sm:text-2xl font-bold mb-3">
Latest Information
</h2>
<div class="rounded-md sm:min-h-[330px]">
<div class="w-full m-auto h-auto max-h-[500px] overflow-x-scroll sm:overflow-hidden sm:overflow-y-scroll scroller ">
<table class="table table-sm table-compact table-pin-rows table-pin-cols w-full">
<thead>
<tr>
<th class="text-white shadow-md font-semibold text-sm text-start bg-default">Drug</th>
<th class="text-white shadow-md font-semibold text-sm text-start bg-default">Stage</th>
<th class="text-white shadow-md font-semibold text-sm text-center bg-default">Phase Status</th>
<th class="text-white shadow-md font-semibold text-sm text-end bg-default">Result</th>
</tr>
</thead>
<tbody>
{#each displayList as item,index}
<tr on:click={() => handleViewData(item)} class="border-y border-gray-800 odd:bg-odd sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] bg-default border-b-[#09090B] cursor-pointer">
<td class="text-white font-medium whitespace-nowrap">
{item["Interventions"]?.length === 0 ? '-' : item["Interventions"]?.length > charNumber ? formatString(item["Interventions"]?.slice(0,charNumber)) + "..." : formatString(item["Interventions"])}
</td>
<td class="text-white font-medium w-full text-start">
{item['Start Date'] === null ? 'n/a' : new Date(item["Start Date"])?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })}
</td>
<td class="text-white text-start font-medium">
{formatString(item['Study Status'])}
</td>
<td class="text-white text-center font-medium">
{item['Phases'] !== 'NA' ? formatString(item["Phases"])?.replace('Phase','') : '-'}
</td>
<td class="text-white text-end font-medium ">
{formatString(item["Study Results"])}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
-->
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md 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 text-gray-400"
></span>
</label>
</div>
</div>
{/if}
{:else}
<div
class="shadow-lg shadow-bg-[#000] bg-[#111112] sm:bg-opacity-[0.5] text-sm sm:text-[1rem] rounded-md w-full p-4 min-h-24 mt-4 text-white m-auto flex justify-center items-center text-center font-semibold"
>
<svg
class="mr-1.5 w-5 h-5 inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#A3A3A3"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/></svg
>
Unlock content with
<a
class="inline-block ml-2 text-blue-400 hover:sm:text-white"
href="/pricing">Pro Subscription</a
>
</div>
{/if}
</main>
</section>
<!-- Put this part before </body> tag -->
<input type="checkbox" id="clinicalDesktopModal" class="modal-toggle" />
<label
for="clinicalDesktopModal"
class="hidden lg:modal modal-bottom sm:modal-middle cursor-pointer"
>
<label for="clinicalDesktopModal" class="cursor-pointer modal-backdrop"
></label>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label
class="modal-box w-full relative bg-primary h-auto max-h-[900px] overflow-y-scroll"
>
<label
for="clinicalDesktopModal"
class="cursor-pointer absolute right-5 top-2 bg-primary text-2xl text-white"
>
</label>
<h3 class="text-xl font-semibold text-white mt-10">
Title: {trialTitle}
</h3>
<p class="py-4 text-gray-200 bg-primary w-full">
<span class="font-semibold text-white">Brief Summary:</span>
{trialSummary}
</p>
<table
class="table table-sm table-compact bg-primary w-full mt-5 mb-10 text-white"
>
<tbody>
<!-- row 1 -->
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">NCT Number</td>
<td class="bg-primary">{trialId}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Start Date</td>
<td class="bg-primary">{trialStart}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">End Date</td>
<td class="bg-primary">{trialEnd}</td>
</tr>
<!-- row 2 -->
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Study Status</td>
<td class="bg-primary">{trialStage}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Phase Status</td>
<td class="bg-primary">{trialPhase}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Study Results</td>
<td class="bg-primary">{trialResult}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Sex</td>
<td class="bg-primary">{formatString(trialSex)}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Age</td>
<td class="bg-primary"
>{formatString(trialAge)?.replace("Older_adult", "Older Adult")}</td
>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Sponsor</td>
<td class="bg-primary">{trialSponsor}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Enrollment</td>
<td class="bg-primary">{trialEnrollment}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Study Type</td>
<td class="bg-primary">{trialStudyType}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Funder Type</td>
<td class="bg-primary">{trialFunderType}</td>
</tr>
<tr class="border-b border-slate-700">
<td class="bg-primary font-semibold">Website</td>
<td class="bg-primary"
><a
class="text-blue-400 sm:hover:text-white"
href={trialLink}
rel="noopener noreferrer"
target="_blank">{trialLink}</a
></td
>
</tr>
</tbody>
</table>
</label>
</label>
<!--Start ETF Modal-->
<div class="lg:hidden drawer drawer-end z-40 overflow-hidden w-screen">
<input id="clinicalMobileModal" type="checkbox" class="drawer-toggle" />
<div class="drawer-side overflow-hidden">
<div class="bg-default min-h-screen w-screen pb-20 overflow-hidden">
<label for="clinicalMobileModal" class="absolute left-6 top-6">
<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
>
</label>
<div class="w-full overflow-hidden overflow-y-scroll p-2">
<h3 class="text-xl font-semibold text-white mt-14 p-3">
Title: {trialTitle}
</h3>
<p class="py-4 text-gray-200 w-full p-3">
<span class="font-semibold text-white">Brief Summary:</span>
{trialSummary}
</p>
<table
class="table table-sm table-compact w-full mt-5 mb-10 text-white"
>
<tbody>
<!-- row 1 -->
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold w-full">NCT Number</td>
<td class="">{trialId}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Start Date</td>
<td class="">{trialStart}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">End Date</td>
<td class="">{trialEnd}</td>
</tr>
<!-- row 2 -->
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Study Status</td>
<td class="">{trialStage}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Phase Status</td>
<td class="">{trialPhase}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Study Results</td>
<td class="">{trialResult}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Sex</td>
<td class="">{formatString(trialSex)}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Age</td>
<td class=""
>{formatString(trialAge)?.replace(
"Older_adult",
"Older Adult",
)}</td
>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Sponsor</td>
<td class="">{trialSponsor}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Enrollment</td>
<td class="">{trialEnrollment}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Study Type</td>
<td class="">{trialStudyType}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Funder Type</td>
<td class="">{trialFunderType}</td>
</tr>
<tr
class="border-b border-slate-700 odd:bg-odd even:bg-default"
>
<td class="font-semibold">Website</td>
<td class=""
><a
class="text-blue-400 sm:hover:text-white"
href={trialLink}
rel="noopener noreferrer"
target="_blank">{trialLink}</a
></td
>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!--End ETF Modal-->
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -1,90 +0,0 @@
<script lang 'ts'></script>
<section class="overflow-hidden text-white h-full">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="lobbyingInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Corporate Lobbying
</label>
<InfoModal
title={"Corporate Lobbying"}
content={"Lobbying the Senate involves special interest groups hiring professional advocates to influence lawmakers and government policies. It is a constitutionally protected activity, but critics argue it can undermine democratic representation by giving disproportionate influence to wealthy and well-organized groups."}
id={"lobbyingInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
<div class="mt-2 pb-8 sm:pb-2 rounded-md bg-default sm:bg-default">
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-1 sm:mt-3 mb-1 w-full">
Explore {$displayCompanyName}'s lobbying strategy by analyzing
their annual spending to influence lawmakers towards favorable
regulation and legislation alignment.
</div>
</div>
<div class="app w-full h-[300px]">
<Chart {init} options={optionsData} class="chart" />
</div>
<div class="w-full text-white text-[1rem] mt-6">
The company allocated an average of {abbreviateNumber(
avgAmount,
true,
)} annually towards lobbying efforts, reaching its peak at {abbreviateNumber(
displayMaxLobbying,
true,
)} in {displayYear}.
</div>
</div>
{:else}
<h2
class="mt-10 mb-5 flex justify-center items-center text-3xl font-bold text-slate-700 m-auto"
>
No data available
<svg
class="w-10 sm:w-12 inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#334155"
d="M18.68 12.32a4.49 4.49 0 0 0-6.36.01a4.49 4.49 0 0 0 0 6.36a4.508 4.508 0 0 0 5.57.63L21 22.39L22.39 21l-3.09-3.11c1.13-1.77.87-4.09-.62-5.57m-1.41 4.95c-.98.98-2.56.97-3.54 0c-.97-.98-.97-2.56.01-3.54c.97-.97 2.55-.97 3.53 0c.97.98.97 2.56 0 3.54M10.9 20.1a6.527 6.527 0 0 1-1.48-2.32C6.27 17.25 4 15.76 4 14v3c0 2.21 3.58 4 8 4c-.4-.26-.77-.56-1.1-.9M4 9v3c0 1.68 2.07 3.12 5 3.7v-.2c0-.93.2-1.85.58-2.69C6.34 12.3 4 10.79 4 9m8-6C7.58 3 4 4.79 4 7c0 2 3 3.68 6.85 4h.05c1.2-1.26 2.86-2 4.6-2c.91 0 1.81.19 2.64.56A3.215 3.215 0 0 0 20 7c0-2.21-3.58-4-8-4Z"
/></svg
>
</h2>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md 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 text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 230px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -6,7 +6,6 @@
abbreviateNumber, abbreviateNumber,
sectorNavigation, sectorNavigation,
} from "$lib/utils"; } from "$lib/utils";
import defaultLogo from "$lib/images/stocks/logo/default_logo.png";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
@ -39,7 +38,7 @@
} }
$: { $: {
if ($etfTicker && typeof window !== "undefined") { if ($etfTicker) {
info = data?.getETFProfile?.at(0); info = data?.getETFProfile?.at(0);
topHoldingList = data?.getETFHoldings?.holdings || []; topHoldingList = data?.getETFHoldings?.holdings || [];
topSectorList = data?.getETFSectorWeighting || []; topSectorList = data?.getETFSectorWeighting || [];
@ -215,21 +214,9 @@
> >
<td class=""> <td class="">
<div class="flex flex-row items-center"> <div class="flex flex-row items-center">
<div <div class="flex flex-col w-full">
class="rounded-full w-10 h-10 relative flex items-center justify-center"
>
<img
style="clip-path: circle(50%);"
class="w-6 h-6 rounded-full"
src={item?.symbol?.length !== 0
? `https://financialmodelingprep.com/image-stock/${item?.symbol}.png`
: defaultLogo}
loading="lazy"
/>
</div>
<div class="flex flex-col ml-3 w-full">
<span class="text-sm font-medium" <span class="text-sm font-medium"
>{item?.symbol ?? "-"}</span >{item?.symbol ?? "n/a"}</span
> >
<span class="text-white text-sm"> <span class="text-white text-sm">
{#if typeof item?.name !== "undefined"} {#if typeof item?.name !== "undefined"}

View File

@ -6,14 +6,12 @@
export let data; export let data;
let rawData = []; let rawData = [];
let rawDataPressRelease; let rawDataPressRelease = [];
let newsList = []; let newsList = [];
let displaySection = "all"; let displaySection = "all";
let isLoaded = true;
async function getPressRelease() { async function getPressRelease() {
isLoaded = false;
displaySection = "press-releases"; displaySection = "press-releases";
const cachedData = getCache($stockTicker, "getPressRelease"); const cachedData = getCache($stockTicker, "getPressRelease");
if (cachedData) { if (cachedData) {
@ -31,7 +29,6 @@
rawDataPressRelease = await response?.json(); rawDataPressRelease = await response?.json();
setCache($stockTicker, rawDataPressRelease, "getPressRelease"); setCache($stockTicker, rawDataPressRelease, "getPressRelease");
} }
isLoaded = true;
} }
function checkIfYoutubeVideo(link: string): string | null { function checkIfYoutubeVideo(link: string): string | null {
@ -67,9 +64,8 @@
: newsList; : newsList;
$: { $: {
if (($stockTicker || $etfTicker) && typeof window !== "undefined") { if ($stockTicker || $etfTicker) {
isLoaded = true; rawData = data?.getNews || [];
rawData = data?.getNews;
rawDataPressRelease = []; rawDataPressRelease = [];
newsList = rawData?.slice(0, 20) ?? []; newsList = rawData?.slice(0, 20) ?? [];
displaySection = "all"; displaySection = "all";
@ -143,41 +139,76 @@
</div> </div>
{#if rawData?.length > 0} {#if rawData?.length > 0}
{#if isLoaded} {#if filteredNewsList?.length > 0}
{#if filteredNewsList?.length > 0} <div class="grid grid-cols-1 gap-2 pb-5 pt-5">
<div class="grid grid-cols-1 gap-2 pb-5 pt-5"> {#each filteredNewsList as item, index}
{#each filteredNewsList as item, index} <div class="w-full flex flex-col bg-default rounded-md m-auto">
<div class="w-full flex flex-col bg-default rounded-md m-auto"> {#if checkIfYoutubeVideo(item.url)}
{#if checkIfYoutubeVideo(item.url)} {#if showVideo[index]}
{#if showVideo[index]} <!-- Show the YouTube iframe when the user clicks play -->
<!-- Show the YouTube iframe when the user clicks play --> <div class="w-full aspect-video mb-4">
<div class="w-full aspect-video mb-4"> <iframe
<iframe class="w-full h-full rounded-md border border-gray-800"
class="w-full h-full rounded-md border border-gray-800" src={`https://www.youtube.com/embed/${checkIfYoutubeVideo(item.url)}`}
src={`https://www.youtube.com/embed/${checkIfYoutubeVideo(item.url)}`} frameborder="0"
frameborder="0" allow="clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allow="clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
allowfullscreen ></iframe>
></iframe> </div>
</div> {:else}
{:else} <!-- Show the image placeholder with a play button -->
<!-- Show the image placeholder with a play button --> <div class="w-full aspect-video">
<div class="w-full aspect-video"> <div class="mb-3 sm:order-3 lg:pr-2">
<div class="mb-3 sm:order-3 lg:pr-2"> <div
class="group relative block cursor-pointer bg-black bg-cover bg-[center_50%] object-contain after:block after:pb-[56.25%] after:content-[''] rounded-sm focus:outline-none focus:ring-2 focus:ring-blue-brand_light focus:ring-offset-2"
style="background-image: url({item?.image});"
tabindex="0"
on:click={() => handlePlayClick(index)}
>
<div <div
class="group relative block cursor-pointer bg-black bg-cover bg-[center_50%] object-contain after:block after:pb-[56.25%] after:content-[''] rounded-sm focus:outline-none focus:ring-2 focus:ring-blue-brand_light focus:ring-offset-2" class="absolute left-[50%] top-[50%] z-10 h-[46px] w-[70px] -translate-x-1/2 -translate-y-1/2 rounded-lg bg-[#212121] opacity-80 transition-all before:absolute before:left-[50%] before:top-[50%] before:-translate-x-1/2 before:-translate-y-1/2 before:border-y-[11px] before:border-l-[19px] before:border-r-0 before:border-transparent before:border-l-white before:content-[''] group-hover:bg-[#ff0000] group-hover:opacity-100"
style="background-image: url({item?.image});" ></div>
tabindex="0"
on:click={() => handlePlayClick(index)}
>
<div
class="absolute left-[50%] top-[50%] z-10 h-[46px] w-[70px] -translate-x-1/2 -translate-y-1/2 rounded-lg bg-[#212121] opacity-80 transition-all before:absolute before:left-[50%] before:top-[50%] before:-translate-x-1/2 before:-translate-y-1/2 before:border-y-[11px] before:border-l-[19px] before:border-r-0 before:border-transparent before:border-l-white before:content-[''] group-hover:bg-[#ff0000] group-hover:opacity-100"
></div>
</div>
</div> </div>
</div> </div>
{/if} </div>
<div class="mt-3 w-full"> {/if}
<div class="mt-3 w-full">
<h3 class="text-sm text-white/80 truncate mb-2">
{formatDate(item?.publishedDate)} &#183; {item?.site}
</h3>
<a
href={item?.url}
rel="noopener noreferrer"
target="_blank"
class="text-lg sm:text-xl font-bold text-white"
>
{item?.title}
<p class="text-white text-sm mt-2 font-normal">
{item?.text?.length > 200
? item?.text?.slice(0, 200) + "..."
: item?.text}
</p>
</a>
</div>
{:else}
<!-- Default news article display -->
<div class="w-full flex flex-col sm:flex-row">
<a
href={item?.url}
rel="noopener noreferrer"
target="_blank"
class="w-full sm:max-w-56 h-fit max-h-96 sm:mr-3 border border-gray-800 rounded-md"
>
<div class="flex-shrink-0 m-auto">
<img
src={item?.image}
class="h-auto w-full rounded-md"
alt="news image"
loading="lazy"
/>
</div>
</a>
<div class="mt-3 sm:mt-0 w-full">
<h3 class="text-sm text-white/80 truncate mb-2"> <h3 class="text-sm text-white/80 truncate mb-2">
{formatDate(item?.publishedDate)} &#183; {item?.site} {formatDate(item?.publishedDate)} &#183; {item?.site}
</h3> </h3>
@ -195,96 +226,48 @@
</p> </p>
</a> </a>
</div> </div>
{:else} </div>
<!-- Default news article display --> {/if}
<div class="w-full flex flex-col sm:flex-row"> </div>
<a <hr class="border-gray-600 w-full m-auto mt-5 mb-5" />
href={item?.url} {/each}
rel="noopener noreferrer" </div>
target="_blank" {:else}
class="w-full sm:max-w-56 h-fit max-h-96 sm:mr-3 border border-gray-800 rounded-md" <div class="mt-5 mt-4 px-3 xs:px-4 sm:px-0">
> <div class="border-l-4 border-white p-4 text-white">
<div class="flex-shrink-0 m-auto"> <div class="flex flex-row items-center">
<img <svg
src={item?.image} class="h-6 w-6"
class="h-auto w-full rounded-md" viewBox="0 0 20 20"
alt="news image" fill="currentColor"
loading="lazy" style="max-width:40px"
/> aria-hidden="true"
</div> ><path
</a> fill-rule="evenodd"
<div class="mt-3 sm:mt-0 w-full"> d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
<h3 class="text-sm text-white/80 truncate mb-2"> clip-rule="evenodd"
{formatDate(item?.publishedDate)} &#183; {item?.site} ></path></svg
</h3> >
<a <div class="ml-3 w-full sm:ml-4">
href={item?.url} <div class="flex w-full flex-row justify-between">
rel="noopener noreferrer" <div>
target="_blank" No {displaySection === "videos" ? "videos" : "articles"} found
class="text-lg sm:text-xl font-bold text-white" for this stock.
>
{item?.title}
<p class="text-white text-sm mt-2 font-normal">
{item?.text?.length > 200
? item?.text?.slice(0, 200) + "..."
: item?.text}
</p>
</a>
</div>
</div>
{/if}
</div>
<hr class="border-gray-600 w-full m-auto mt-5 mb-5" />
{/each}
</div>
{:else}
<div class="mt-5 mt-4 px-3 xs:px-4 sm:px-0">
<div class="border-l-4 border-white p-4 text-white">
<div class="flex flex-row items-center">
<svg
class="h-6 w-6"
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:40px"
aria-hidden="true"
><path
fill-rule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clip-rule="evenodd"
></path></svg
>
<div class="ml-3 w-full sm:ml-4">
<div class="flex w-full flex-row justify-between">
<div>
No {displaySection === "videos" ? "videos" : "articles"} found
for this stock.
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/if}
{#if newsList?.length !== rawData?.length && filteredNewsList?.length > 0 && displaySection === "all"}
<label
on:click={loadMoreData}
class="shadow-lg rounded-md cursor-pointer w-5/6 sm:w-full flex justify-center items-center py-3 h-full text-sm sm:text-[1rem] text-center font-semibold text-black m-auto sm:hover:bg-gray-300 bg-[#fff]"
>
Load More News
</label>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md 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 text-gray-400"
></span>
</label>
</div>
</div> </div>
{/if} {/if}
{#if newsList?.length !== rawData?.length && filteredNewsList?.length > 0 && displaySection === "all"}
<label
on:click={loadMoreData}
class="shadow-lg rounded-md cursor-pointer w-5/6 sm:w-full flex justify-center items-center py-3 h-full text-sm sm:text-[1rem] text-center font-semibold text-black m-auto sm:hover:bg-gray-300 bg-[#fff]"
>
Load More News
</label>
{/if}
{:else} {:else}
<span class="text-white"> No News article available yet </span> <span class="text-white"> No News article available yet </span>
{/if} {/if}

View File

@ -20,7 +20,7 @@
} }
$: { $: {
if ($stockTicker && typeof window !== "undefined") { if ($stockTicker) {
rawData = data?.getNextEarnings?.next; rawData = data?.getNextEarnings?.next;
epsRatio = epsRatio =
rawData?.epsPrior !== 0 rawData?.epsPrior !== 0

View File

@ -5,8 +5,6 @@
export let userTier; export let userTier;
export let rawData; export let rawData;
let isLoaded = false;
let xData = []; let xData = [];
let tableRevenue = []; let tableRevenue = [];
@ -65,10 +63,8 @@
} }
$: { $: {
if ($stockTicker && typeof window !== "undefined") { if ($stockTicker) {
isLoaded = false;
prepareDataset(); prepareDataset();
isLoaded = true;
} }
} }
</script> </script>
@ -76,182 +72,163 @@
<section class="overflow-hidden text-white h-full pb-8 sm:pb-2"> <section class="overflow-hidden text-white h-full pb-8 sm:pb-2">
<main class="overflow-hidden"> <main class="overflow-hidden">
<div class="w-full m-auto"> <div class="w-full m-auto">
{#if isLoaded} {#if rawData?.length !== 0}
{#if rawData?.length !== 0} <span class="">
<span class=""> The average price volatility over this 3-day period is
The average price volatility over this 3-day period is {#if userTier !== "Pro"}
{#if userTier !== "Pro"} ... Unlock content with
... Unlock content with <a
<a class="inline-block ml-0.5 text-blue-400 sm:hover:text-white"
class="inline-block ml-0.5 text-blue-400 sm:hover:text-white" href="/pricing"
href="/pricing" >Pro Subscription <svg
>Pro Subscription <svg class="w-4 h-4 mb-1 inline-block text[#A3A3A3] sm:hover:text-white"
class="w-4 h-4 mb-1 inline-block text[#A3A3A3] sm:hover:text-white" xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
viewBox="0 0 24 24" ><path
><path fill="currentColor"
fill="currentColor" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z" /></svg
/></svg ></a
></a >
> {:else}
{:else} <span class="font-bold">±{averageVolatility?.toFixed(2)}%</span>.
<span class="font-bold">±{averageVolatility?.toFixed(2)}%</span>. During this period, the reported revenue exceeded expectations
During this period, the reported revenue exceeded expectations <span class="font-bold">{positiveRevenueSurprisePercentage}%</span>
<span class="font-bold">{positiveRevenueSurprisePercentage}%</span of the time & the reported EPS surpassed analyst estimates
> <span class="font-bold">{positiveEpsSurprisePercentage}%</span> of the
of the time & the reported EPS surpassed analyst estimates time.
<span class="font-bold">{positiveEpsSurprisePercentage}%</span> of {/if}
the time. </span>
{/if} <div
</span> class="no-scrollbar flex justify-start items-center w-screen sm:w-full mt-3 m-auto overflow-x-scroll pr-5 sm:pr-0"
<div >
class="no-scrollbar flex justify-start items-center w-screen sm:w-full mt-3 m-auto overflow-x-scroll pr-5 sm:pr-0" <table
class="table table-sm table-pin-cols table-compact rounded-none sm:rounded-md w-full bg-table border border-gray-800"
> >
<table <thead class="">
class="table table-sm table-pin-cols table-compact rounded-none sm:rounded-md w-full bg-table border border-gray-800" <tr class="">
> <th
<thead class=""> class="bg-primary border-b border-[#000] text-white font-semibold text-sm text-start"
<tr class=""> >Date</th
<th >
class="bg-primary border-b border-[#000] text-white font-semibold text-sm text-start" {#each xData as item}
>Date</th <td
class="z-20 bg-primary border-b border-[#000] text-white font-semibold text-sm text-end bg-default"
>{item}</td
> >
{#each xData as item} {/each}
<td </tr>
class="z-20 bg-primary border-b border-[#000] text-white font-semibold text-sm text-end bg-default" </thead>
>{item}</td <tbody class="shadow-md">
> <tr class="bg-primary border-b-[#27272A]">
{/each} <th
</tr> class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
</thead> >
<tbody class="shadow-md"> Reported Revenue
<tr class="bg-primary border-b-[#27272A]"> </th>
<th {#each tableRevenue as item, index}
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]" <td
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default"
> >
Reported Revenue {#if index !== 0}
</th> {#if userTier !== "Pro"}
{#each tableRevenue as item, index} <a
<td class="inline-block ml-0.5 text-white whitespace-nowrap"
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default" href="/pricing"
> >
{#if index !== 0} Pro
{#if userTier !== "Pro"} <svg
<a class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
class="inline-block ml-0.5 text-white whitespace-nowrap" xmlns="http://www.w3.org/2000/svg"
href="/pricing" viewBox="0 0 24 24"
> >
Pro <path
<svg fill="currentColor"
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
xmlns="http://www.w3.org/2000/svg" />
viewBox="0 0 24 24" </svg>
> </a>
<path {:else if item !== undefined && item !== null}
fill="currentColor" <span>{@html abbreviateNumber(item, false, true)}</span>
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span
>{@html abbreviateNumber(item, false, true)}</span
>
{:else}
n/a
{/if}
{:else} {:else}
{@html abbreviateNumber(item, false, true)} n/a
{/if} {/if}
</td> {:else}
{/each} {@html abbreviateNumber(item, false, true)}
</tr> {/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]"> <tr class="bg-primary border-b-[#27272A]">
<th <th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]" class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Est. Revenue
</th>
{#each tableRevenueEst as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default"
> >
Est. Revenue {#if index !== 0}
</th> {#if userTier !== "Pro"}
{#each tableRevenueEst as item, index} <a
<td class="inline-block ml-0.5 text-white whitespace-nowrap"
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default" href="/pricing"
> >
{#if index !== 0} Pro
{#if userTier !== "Pro"} <svg
<a class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
class="inline-block ml-0.5 text-white whitespace-nowrap" xmlns="http://www.w3.org/2000/svg"
href="/pricing" viewBox="0 0 24 24"
> >
Pro <path
<svg fill="currentColor"
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
xmlns="http://www.w3.org/2000/svg" />
viewBox="0 0 24 24" </svg>
> </a>
<path {:else if item !== undefined && item !== null}
fill="currentColor" <span>{@html abbreviateNumber(item, false, true)}</span>
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span
>{@html abbreviateNumber(item, false, true)}</span
>
{:else}
n/a
{/if}
{:else} {:else}
{@html abbreviateNumber(item, false, true)} n/a
{/if} {/if}
</td> {:else}
{/each} {@html abbreviateNumber(item, false, true)}
</tr> {/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]"> <tr class="bg-primary border-b-[#27272A]">
<th <th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]" class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Revenue Surprise
</th>
{#each tableRevenueSurprise as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-semibold bg-default"
> >
Revenue Surprise {#if index !== 0}
</th> {#if userTier !== "Pro"}
{#each tableRevenueSurprise as item, index} <a
<td class="inline-block ml-0.5 text-white whitespace-nowrap font-normal"
class="text-white text-sm sm:text-[1rem] text-end font-semibold bg-default" href="/pricing"
> >
{#if index !== 0} Pro
{#if userTier !== "Pro"} <svg
<a class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
class="inline-block ml-0.5 text-white whitespace-nowrap font-normal" xmlns="http://www.w3.org/2000/svg"
href="/pricing" viewBox="0 0 24 24"
> >
Pro <path
<svg fill="currentColor"
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
xmlns="http://www.w3.org/2000/svg" />
viewBox="0 0 24 24" </svg>
> </a>
<path {:else if item !== undefined && item !== null}
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span
class={item > 0
? "text-[#00FC50] before:content-['+']"
: item < 0
? "text-[#FF2F1F]"
: ""}
>
{abbreviateNumber(item)}%
</span>
{:else}
n/a
{/if}
{:else}
<span <span
class={item > 0 class={item > 0
? "text-[#00FC50] before:content-['+']" ? "text-[#00FC50] before:content-['+']"
@ -261,133 +238,133 @@
> >
{abbreviateNumber(item)}% {abbreviateNumber(item)}%
</span> </span>
{/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]">
<th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Reported EPS
</th>
{#each tableEPS as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default"
>
{#if index !== 0}
{#if userTier !== "Pro"}
<a
class="inline-block ml-0.5 text-white whitespace-nowrap"
href="/pricing"
>
Pro
<svg
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span>{abbreviateNumber(item)}</span>
{:else}
n/a
{/if}
{:else} {:else}
{abbreviateNumber(item)} n/a
{/if} {/if}
</td> {:else}
{/each} <span
</tr> class={item > 0
? "text-[#00FC50] before:content-['+']"
: item < 0
? "text-[#FF2F1F]"
: ""}
>
{abbreviateNumber(item)}%
</span>
{/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]"> <tr class="bg-primary border-b-[#27272A]">
<th <th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]" class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Reported EPS
</th>
{#each tableEPS as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default"
> >
Est. EPS {#if index !== 0}
</th> {#if userTier !== "Pro"}
{#each tableEPSEst as item, index} <a
<td class="inline-block ml-0.5 text-white whitespace-nowrap"
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default" href="/pricing"
> >
{#if index !== 0} Pro
{#if userTier !== "Pro"} <svg
<a class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
class="inline-block ml-0.5 text-white whitespace-nowrap" xmlns="http://www.w3.org/2000/svg"
href="/pricing" viewBox="0 0 24 24"
> >
Pro <path
<svg fill="currentColor"
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
xmlns="http://www.w3.org/2000/svg" />
viewBox="0 0 24 24" </svg>
> </a>
<path {:else if item !== undefined && item !== null}
fill="currentColor" <span>{abbreviateNumber(item)}</span>
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span>{abbreviateNumber(item)}</span>
{:else}
n/a
{/if}
{:else} {:else}
{abbreviateNumber(item)} n/a
{/if} {/if}
</td> {:else}
{/each} {abbreviateNumber(item)}
</tr> {/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]"> <tr class="bg-primary border-b-[#27272A]">
<th <th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]" class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Est. EPS
</th>
{#each tableEPSEst as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-default"
> >
EPS Surprise {#if index !== 0}
</th> {#if userTier !== "Pro"}
{#each tableEPSSurprise as item, index} <a
<td class="inline-block ml-0.5 text-white whitespace-nowrap"
class="text-white text-sm sm:text-[1rem] text-end font-semibold bg-default" href="/pricing"
> >
{#if index !== 0} Pro
{#if userTier !== "Pro"} <svg
<a class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
class="inline-block ml-0.5 text-white whitespace-nowrap font-normal" xmlns="http://www.w3.org/2000/svg"
href="/pricing" viewBox="0 0 24 24"
> >
Pro <path
<svg fill="currentColor"
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
xmlns="http://www.w3.org/2000/svg" />
viewBox="0 0 24 24" </svg>
> </a>
<path {:else if item !== undefined && item !== null}
fill="currentColor" <span>{abbreviateNumber(item)}</span>
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span
class={item > 0
? "text-[#00FC50] before:content-['+']"
: item < 0
? "text-[#FF2F1F]"
: ""}
>
{abbreviateNumber(item)}%
</span>
{:else}
n/a
{/if}
{:else} {:else}
n/a
{/if}
{:else}
{abbreviateNumber(item)}
{/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]">
<th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
EPS Surprise
</th>
{#each tableEPSSurprise as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-semibold bg-default"
>
{#if index !== 0}
{#if userTier !== "Pro"}
<a
class="inline-block ml-0.5 text-white whitespace-nowrap font-normal"
href="/pricing"
>
Pro
<svg
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
<span <span
class={item > 0 class={item > 0
? "text-[#00FC50] before:content-['+']" ? "text-[#00FC50] before:content-['+']"
@ -397,64 +374,65 @@
> >
{abbreviateNumber(item)}% {abbreviateNumber(item)}%
</span> </span>
{/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]">
<th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Volatility
</th>
{#each tableVolatility as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-semibold bg-default"
>
{#if index !== 0}
{#if userTier !== "Pro"}
<a
class="inline-block ml-0.5 text-white whitespace-nowrap font-normal"
href="/pricing"
>
Pro
<svg
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
±{abbreviateNumber(item)}%
{:else}
n/a
{/if}
{:else} {:else}
n/a
{/if}
{:else}
<span
class={item > 0
? "text-[#00FC50] before:content-['+']"
: item < 0
? "text-[#FF2F1F]"
: ""}
>
{abbreviateNumber(item)}%
</span>
{/if}
</td>
{/each}
</tr>
<tr class="bg-primary border-b-[#27272A]">
<th
class="bg-primary whitespace-nowrap text-sm sm:text-[1rem] text-white text-start font-medium border-b border-[#27272A]"
>
Volatility
</th>
{#each tableVolatility as item, index}
<td
class="text-white text-sm sm:text-[1rem] text-end font-semibold bg-default"
>
{#if index !== 0}
{#if userTier !== "Pro"}
<a
class="inline-block ml-0.5 text-white whitespace-nowrap font-normal"
href="/pricing"
>
Pro
<svg
class="w-4 h-4 ml-0.5 mb-1 inline-block text-[#A3A3A3]"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/>
</svg>
</a>
{:else if item !== undefined && item !== null}
±{abbreviateNumber(item)}% ±{abbreviateNumber(item)}%
{:else}
n/a
{/if} {/if}
</td> {:else}
{/each} ±{abbreviateNumber(item)}%
</tr> {/if}
</tbody> </td>
</table> {/each}
</div> </tr>
{/if} </tbody>
{:else} </table>
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md 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 text-gray-400"
></span>
</label>
</div>
</div> </div>
{/if} {/if}
</div> </div>

View File

@ -34,7 +34,7 @@
} }
$: { $: {
if ($stockTicker && typeof window !== "undefined") { if ($stockTicker) {
info = data?.getStockDeck; info = data?.getStockDeck;
ipoDate = ipoDate =
info?.ipoDate !== null && info?.ipoDate?.length > 0 info?.ipoDate !== null && info?.ipoDate?.length > 0

View File

@ -6,8 +6,7 @@
export let data; export let data;
let isLoaded = false; let wiim = [];
let wiim;
let showFullHistory = false; let showFullHistory = false;
function latestInfoDate(inputDate) { function latestInfoDate(inputDate) {
@ -36,11 +35,9 @@
} }
$: { $: {
if ((get(stockTicker) || get(etfTicker)) && typeof window !== "undefined") { if (get(stockTicker) || get(etfTicker)) {
isLoaded = false;
showFullHistory = false; showFullHistory = false;
wiim = data?.getWhyPriceMoved || []; wiim = data?.getWhyPriceMoved || [];
isLoaded = true;
} }
} }
</script> </script>
@ -61,185 +58,172 @@
/> />
</div> </div>
{#if isLoaded} {#if wiim?.length !== 0}
{#if wiim?.length !== 0} <div class="mt-5">
<div class="mt-5"> {#each showFullHistory ? wiim : wiim?.slice(0, 2) as item, index}
{#each showFullHistory ? wiim : wiim?.slice(0, 2) as item, index} <div
<div class="w-full {index === 1 && !showFullHistory && wiim?.length > 2
class="w-full {index === 1 && !showFullHistory && wiim?.length > 2 ? 'opacity-[0.5]'
? 'opacity-[0.5]' : ''} "
: ''} " >
> <div class="relative">
<div class="relative"> <div class="">
<div class=""> <div class="flex justify-center">
<div class="flex justify-center"> <!--Start Item-->
<!--Start Item--> <div class="flex flex-row items-center w-full mb-8">
<div class="flex flex-row items-center w-full mb-8"> <!-- Vertical Line -->
<!-- Vertical Line --> <div
<div class="w-1 h-full mr-4 rounded-lg {item?.changesPercentage ===
class="w-1 h-full mr-4 rounded-lg {item?.changesPercentage === '-'
'-' ? 'bg-white'
? 'bg-white' : item?.changesPercentage >= 0
: item?.changesPercentage >= 0 ? 'bg-[#00FC50]'
? 'bg-[#00FC50]' : 'bg-[#FF2F1F]'}"
: 'bg-[#FF2F1F]'}" ></div>
></div> <!-- Item Content -->
<!-- Item Content -->
<div class="w-full h-full"> <div class="w-full h-full">
<div class="flex flex-col items-start"> <div class="flex flex-col items-start">
<div class="flex flex-row items-start w-full"> <div class="flex flex-row items-start w-full">
<span class="text-white text-sm" <span class="text-white text-sm"
>{formatDate(item?.date)} &#183; >{formatDate(item?.date)} &#183;
<a <a
href={item?.url} href={item?.url}
class="inline-block text-sm text-white sm:hover:underline sm:hover:underline-offset-4" class="inline-block text-sm text-white sm:hover:underline sm:hover:underline-offset-4"
>
Source
</a></span
> >
{#if latestInfoDate(item?.date)} Source
<label </a></span
class="bg-[#fff] rounded text-black font-semibold text-xs px-2 py-0.5 ml-3" >
>New</label {#if latestInfoDate(item?.date)}
> <label
{/if} class="bg-[#fff] rounded text-black font-semibold text-xs px-2 py-0.5 ml-3"
<div >New</label
class="text-white text-sm sm:text-[1rem] ml-auto font-medium"
> >
{#if item?.changesPercentage >= 0} {/if}
<span class="text-[#00FC50] inline-block" <div
>+{item?.changesPercentage}%</span class="text-white text-sm sm:text-[1rem] ml-auto font-medium"
> >
<svg {#if item?.changesPercentage >= 0}
class="w-5 h-5 hidden sm:inline-block" <span class="text-[#00FC50] inline-block"
viewBox="0 0 24 24" >+{item?.changesPercentage}%</span
fill="none" >
xmlns="http://www.w3.org/2000/svg" <svg
transform="rotate(180)matrix(-1, 0, 0, 1, 0, 0)" class="w-5 h-5 hidden sm:inline-block"
><g id="SVGRepo_bgCarrier" stroke-width="0" viewBox="0 0 24 24"
></g><g fill="none"
id="SVGRepo_tracerCarrier" xmlns="http://www.w3.org/2000/svg"
stroke-linecap="round" transform="rotate(180)matrix(-1, 0, 0, 1, 0, 0)"
stroke-linejoin="round" ><g id="SVGRepo_bgCarrier" stroke-width="0"
></g><g id="SVGRepo_iconCarrier"> ></g><g
<g clip-path="url(#clip0_1076_36065)"> id="SVGRepo_tracerCarrier"
<path stroke-linecap="round"
d="M1.70711 5.29289C1.31658 4.90237 0.683417 4.90237 0.292893 5.29289C-0.0976311 5.68342 -0.0976311 6.31658 0.292893 6.70711L7.79289 14.2071C8.18342 14.5976 8.81658 14.5976 9.20711 14.2071L13.5 9.91421L20.5858 17H17C16.4477 17 16 17.4477 16 18C16 18.5523 16.4477 19 17 19H22.9993L23.003 19C23.1375 18.9996 23.2657 18.9727 23.3828 18.9241C23.5007 18.8753 23.6112 18.803 23.7071 18.7071C23.8902 18.524 23.9874 18.2877 23.9989 18.048C23.9996 18.032 24 18.016 24 18V12C24 11.4477 23.5523 11 23 11C22.4477 11 22 11.4477 22 12V15.5858L14.2071 7.79289C13.8166 7.40237 13.1834 7.40237 12.7929 7.79289L8.5 12.0858L1.70711 5.29289Z" stroke-linejoin="round"
></g><g id="SVGRepo_iconCarrier">
<g clip-path="url(#clip0_1076_36065)">
<path
d="M1.70711 5.29289C1.31658 4.90237 0.683417 4.90237 0.292893 5.29289C-0.0976311 5.68342 -0.0976311 6.31658 0.292893 6.70711L7.79289 14.2071C8.18342 14.5976 8.81658 14.5976 9.20711 14.2071L13.5 9.91421L20.5858 17H17C16.4477 17 16 17.4477 16 18C16 18.5523 16.4477 19 17 19H22.9993L23.003 19C23.1375 18.9996 23.2657 18.9727 23.3828 18.9241C23.5007 18.8753 23.6112 18.803 23.7071 18.7071C23.8902 18.524 23.9874 18.2877 23.9989 18.048C23.9996 18.032 24 18.016 24 18V12C24 11.4477 23.5523 11 23 11C22.4477 11 22 11.4477 22 12V15.5858L14.2071 7.79289C13.8166 7.40237 13.1834 7.40237 12.7929 7.79289L8.5 12.0858L1.70711 5.29289Z"
fill="#00FC50"
></path>
</g>
<defs>
<clipPath id="clip0_1076_36065">
<rect
width="24"
height="24"
fill="#00FC50" fill="#00FC50"
></path> ></rect>
</g> </clipPath>
<defs> </defs>
<clipPath id="clip0_1076_36065"> </g></svg
<rect >
width="24" {:else if item?.changesPercentage < 0}
height="24" <span class="text-[#FF2F1F] inline-block"
fill="#00FC50" >{item?.changesPercentage}%
></rect> </span>
</clipPath> <svg
</defs> class="w-5 h-5 hidden sm:inline-block"
</g></svg viewBox="0 0 24 24"
> fill="#FF2F1F"
{:else if item?.changesPercentage < 0} xmlns="http://www.w3.org/2000/svg"
<span class="text-[#FF2F1F] inline-block" ><g id="SVGRepo_bgCarrier" stroke-width="0"
>{item?.changesPercentage}% ></g><g
</span> id="SVGRepo_tracerCarrier"
<svg stroke-linecap="round"
class="w-5 h-5 hidden sm:inline-block" stroke-linejoin="round"
viewBox="0 0 24 24" ></g><g id="SVGRepo_iconCarrier">
fill="#FF2F1F" <g clip-path="url(#clip0_1076_36065)">
xmlns="http://www.w3.org/2000/svg" <path
><g id="SVGRepo_bgCarrier" stroke-width="0" d="M1.70711 5.29289C1.31658 4.90237 0.683417 4.90237 0.292893 5.29289C-0.0976311 5.68342 -0.0976311 6.31658 0.292893 6.70711L7.79289 14.2071C8.18342 14.5976 8.81658 14.5976 9.20711 14.2071L13.5 9.91421L20.5858 17H17C16.4477 17 16 17.4477 16 18C16 18.5523 16.4477 19 17 19H22.9993L23.003 19C23.1375 18.9996 23.2657 18.9727 23.3828 18.9241C23.5007 18.8753 23.6112 18.803 23.7071 18.7071C23.8902 18.524 23.9874 18.2877 23.9989 18.048C23.9996 18.032 24 18.016 24 18V12C24 11.4477 23.5523 11 23 11C22.4477 11 22 11.4477 22 12V15.5858L14.2071 7.79289C13.8166 7.40237 13.1834 7.40237 12.7929 7.79289L8.5 12.0858L1.70711 5.29289Z"
></g><g fill="#FF2F1F"
id="SVGRepo_tracerCarrier" ></path>
stroke-linecap="round" </g>
stroke-linejoin="round" <defs>
></g><g id="SVGRepo_iconCarrier"> <clipPath id="clip0_1076_36065">
<g clip-path="url(#clip0_1076_36065)"> <rect
<path width="24"
d="M1.70711 5.29289C1.31658 4.90237 0.683417 4.90237 0.292893 5.29289C-0.0976311 5.68342 -0.0976311 6.31658 0.292893 6.70711L7.79289 14.2071C8.18342 14.5976 8.81658 14.5976 9.20711 14.2071L13.5 9.91421L20.5858 17H17C16.4477 17 16 17.4477 16 18C16 18.5523 16.4477 19 17 19H22.9993L23.003 19C23.1375 18.9996 23.2657 18.9727 23.3828 18.9241C23.5007 18.8753 23.6112 18.803 23.7071 18.7071C23.8902 18.524 23.9874 18.2877 23.9989 18.048C23.9996 18.032 24 18.016 24 18V12C24 11.4477 23.5523 11 23 11C22.4477 11 22 11.4477 22 12V15.5858L14.2071 7.79289C13.8166 7.40237 13.1834 7.40237 12.7929 7.79289L8.5 12.0858L1.70711 5.29289Z" height="24"
fill="#FF2F1F" fill="#FF2F1F"
></path> ></rect>
</g> </clipPath>
<defs> </defs>
<clipPath id="clip0_1076_36065"> </g></svg
<rect >
width="24"
height="24"
fill="#FF2F1F"
></rect>
</clipPath>
</defs>
</g></svg
>
{/if}
</div>
</div>
<div class="flex flex-col w-full pt-2">
{#if index === 0 && data?.user?.tier !== "Pro"}
<span class="mt-3">
{item?.text?.slice(0, 50) + "..."}
Unlock content with
<a
class="inline-block ml-0.5 text-blue-400 sm:hover:text-white"
href="/pricing"
>Pro Subscription <svg
class="w-4 h-4 mb-1 inline-block text[#A3A3A3] sm:hover:text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/></svg
></a
>
</span>
{:else}
<span class="text-white text-[1rem]">
{item?.text}
</span>
{/if} {/if}
</div> </div>
</div> </div>
<div class="flex flex-col w-full pt-2">
{#if index === 0 && data?.user?.tier !== "Pro"}
<span class="mt-3">
{item?.text?.slice(0, 50) + "..."}
Unlock content with
<a
class="inline-block ml-0.5 text-blue-400 sm:hover:text-white"
href="/pricing"
>Pro Subscription <svg
class="w-4 h-4 mb-1 inline-block text[#A3A3A3] sm:hover:text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="currentColor"
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
/></svg
></a
>
</span>
{:else}
<span class="text-white text-[1rem]">
{item?.text}
</span>
{/if}
</div>
</div> </div>
</div> </div>
<!--End Item-->
</div> </div>
<!--End Item-->
</div> </div>
</div> </div>
</div> </div>
{/each} </div>
</div> {/each}
{#if wiim?.length > 2}
<label
on:click={() => (showFullHistory = !showFullHistory)}
class="cursor-pointer flex justify-center items-center mt-5"
>
<svg
class="w-10 h-10 transform {showFullHistory ? 'rotate-180' : ''} "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#2A323C"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 13.5L7.5 11l1.42-1.41L12 12.67l3.08-3.08L16.5 11L12 15.5z"
/></svg
>
</label>
{/if}
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-default 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 text-gray-400"
></span>
</label>
</div>
</div> </div>
{#if wiim?.length > 2}
<label
on:click={() => (showFullHistory = !showFullHistory)}
class="cursor-pointer flex justify-center items-center mt-5"
>
<svg
class="w-10 h-10 transform {showFullHistory ? 'rotate-180' : ''} "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#2A323C"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 13.5L7.5 11l1.42-1.41L12 12.67l3.08-3.08L16.5 11L12 15.5z"
/></svg
>
</label>
{/if}
{/if} {/if}
</main> </main>
</section> </section>

View File

@ -44,15 +44,15 @@ const fetchWatchlist = async (pb, userId) => {
export const load = async ({ params, locals }) => { export const load = async ({ params, locals }) => {
const { apiURL, apiKey, pb, user } = locals; const { apiURL, apiKey, pb, user } = locals;
const { tickerID } = params; const { tickerID } = params;
const endpoints = [ const endpoints = [
"/etf-profile", "/etf-profile",
"/etf-holdings", "/etf-holdings",
"/etf-sector-weighting", "/etf-sector-weighting",
"/stock-dividend", "/stock-dividend",
"/stock-quote", "/stock-quote",
"/pre-post-quote",
"/wiim", "/wiim",
"/one-day-price", "/one-day-price",
"/stock-news", "/stock-news",
@ -66,23 +66,27 @@ export const load = async ({ params, locals }) => {
]; ];
const [ const [
getETFProfile, getETFProfile,
getETFHoldings, getETFHoldings,
getETFSectorWeighting, getETFSectorWeighting,
getStockDividend, getStockDividend,
getStockQuote, getStockQuote,
getPrePostQuote,
getWhyPriceMoved, getWhyPriceMoved,
getOneDayPrice, getOneDayPrice,
getNews, getNews,
getUserWatchlist, getUserWatchlist,
] = await Promise.all(promises); ] = await Promise.all(promises);
return { return {
getETFProfile, getETFProfile,
getETFHoldings, getETFHoldings,
getETFSectorWeighting, getETFSectorWeighting,
getStockDividend, getStockDividend,
getStockQuote, getStockQuote,
getPrePostQuote,
getWhyPriceMoved, getWhyPriceMoved,
getOneDayPrice, getOneDayPrice,
getNews, getNews,

View File

@ -22,16 +22,21 @@
import { onMount, onDestroy, afterUpdate } from "svelte"; import { onMount, onDestroy, afterUpdate } from "svelte";
import { page } from "$app/stores"; import { page } from "$app/stores";
import toast from "svelte-french-toast"; import toast from "svelte-french-toast";
import Markethour from "$lib/components/Markethour.svelte"; import { convertTimestamp } from "$lib/utils";
import PriceAlert from "$lib/components/PriceAlert.svelte"; import PriceAlert from "$lib/components/PriceAlert.svelte";
export let data; export let data;
let prePostData = data?.getPrePostQuote || {};
$: $realtimePrice = data?.getStockQuote?.price?.toFixed(2); $: $realtimePrice = data?.getStockQuote?.price?.toFixed(2);
let oneDayPrice = [];
let previousRealtimePrice = null; let previousRealtimePrice = null;
let previousTicker; let previousTicker;
let socket; let socket;
$etfTicker = data?.getParams;
$assetType = "stock";
$displayCompanyName = data?.companyName;
let isScrolled = false; let isScrolled = false;
let y; let y;
@ -42,16 +47,17 @@
//let availableCash = 0; //let availableCash = 0;
let displaySection = ""; let displaySection = "";
let displayLegend = {};
function shareContent(url) { function shareContent(url) {
if (navigator.share) { if (navigator.share) {
navigator navigator
.share({ ?.share({
title: document.title, title: document.title,
url, url,
}) })
.then(() => console.log("Content shared successfully.")) ?.then(() => console.log("Content shared successfully."))
.catch((error) => console.log("Error sharing content:", error)); ?.catch((error) => console.log("Error sharing content:", error));
} else { } else {
toast.error("Sharing is not supported by your device", { toast.error("Sharing is not supported by your device", {
style: "background: #2A2E39; color: #fff;", style: "background: #2A2E39; color: #fff;",
@ -65,7 +71,12 @@
options: "/options", options: "/options",
"dark-pool": "/dark-pool", "dark-pool": "/dark-pool",
dividends: "/dividends", dividends: "/dividends",
statistics: "/statistics",
metrics: "metrics",
forecast: "/forecast",
financials: "/financials",
history: "/history", history: "/history",
profile: "/profile",
}; };
if (state !== "overview" && sectionMap[state]) { if (state !== "overview" && sectionMap[state]) {
@ -241,14 +252,74 @@
}); });
$: { $: {
if ( if ($etfTicker && $etfTicker?.length !== 0) {
$etfTicker &&
$etfTicker?.length !== 0 &&
typeof window !== "undefined"
) {
// add a check to see if running on client-side // add a check to see if running on client-side
$etfTicker = data?.getParams;
$assetType = "stock";
$displayCompanyName = data?.companyName;
$currentPortfolioPrice = data?.getStockQuote?.price; $currentPortfolioPrice = data?.getStockQuote?.price;
prePostData = data?.getPrePostQuote || {};
const output = [...data?.getOneDayPrice] ?? [];
oneDayPrice = output?.map((item) => ({
time: Date?.parse(item?.time + "Z") / 1000,
open: item?.open !== null ? item?.open : NaN,
high: item?.high !== null ? item?.high : NaN,
low: item?.low !== null ? item?.low : NaN,
close: item?.close !== null ? item?.close : NaN,
}));
let change;
let currentDataRowOneDay;
let baseClose =
data?.getStockQuote?.previousClose || oneDayPrice?.at(0)?.open;
const length = oneDayPrice?.length;
for (let i = length - 1; i >= 0; i--) {
if (!isNaN(oneDayPrice[i]?.close)) {
currentDataRowOneDay = oneDayPrice[i];
break;
}
}
// Calculate percentage change if baseClose and currentDataRow are valid
const closeValue =
$realtimePrice !== null && $realtimePrice !== undefined
? $realtimePrice
: currentDataRowOneDay?.close || currentDataRowOneDay?.value;
if (closeValue && baseClose) {
change = ((closeValue / baseClose - 1) * 100)?.toFixed(2);
}
// Format date
const date = new Date(currentDataRowOneDay?.time * 1000);
const options = {
day: "2-digit",
month: "short",
year: "numeric",
hour: "numeric",
minute: "2-digit",
timeZone: "UTC",
};
const formattedDate = date?.toLocaleString("en-US", options);
const safeFormattedDate =
formattedDate === "Invalid Date"
? convertTimestamp(data?.getStockQuote?.timestamp)
: formattedDate;
// Set display legend
displayLegend = {
close:
$realtimePrice !== null && $realtimePrice !== undefined
? $realtimePrice
: currentDataRowOneDay?.close?.toFixed(2) ||
data?.getStockQuote?.price?.toFixed(2),
date: safeFormattedDate,
change,
};
} }
} }
@ -256,24 +327,21 @@
(item) => item.user === data?.user?.id && item.ticker?.includes($etfTicker), (item) => item.user === data?.user?.id && item.ticker?.includes($etfTicker),
); );
$: charNumber = $screenWidth < 640 ? 15 : 25; $: charNumber = $screenWidth < 640 ? 25 : 40;
$: { $: {
if ( if ($etfTicker && $page.url.pathname === `/etf/${$etfTicker}`) {
$etfTicker &&
typeof window !== "undefined" &&
$page.url.pathname === `/etf/${$etfTicker}`
) {
displaySection = "overview"; displaySection = "overview";
} }
} }
$: { $: {
if ($page?.url?.pathname && typeof window !== "undefined") { if ($page?.url?.pathname) {
const parts = $page?.url?.pathname?.split("/"); const parts = $page?.url?.pathname?.split("/");
const sectionMap = { const sectionMap = {
holdings: "holdings", holdings: "holdings",
options: "options", options: "options",
options: "options",
"dark-pool": "dark-pool", "dark-pool": "dark-pool",
insider: "insider", insider: "insider",
dividends: "dividends", dividends: "dividends",
@ -292,10 +360,10 @@
<svelte:window bind:scrollY={y} /> <svelte:window bind:scrollY={y} />
<body <body
class="bg-default w-full max-w-screen sm:max-w-7xl min-h-screen sm:max-w-[1400px] overflow-hidden" class="bg-default w-full max-w-screen sm:max-w-[1400px] min-h-screen overflow-hidden"
> >
<!-- Page wrapper --> <!-- Page wrapper -->
<div class="flex flex-col w-full mt-5 relative w-full"> <div class="mt-5 flex flex-col w-full relative w-full">
<main class="grow w-full"> <main class="grow w-full">
<section class="w-full"> <section class="w-full">
<div class="w-full"> <div class="w-full">
@ -330,9 +398,7 @@
? "hidden" ? "hidden"
: "flex flex-col items-center ml-6 transition-transform ease-in"} : "flex flex-col items-center ml-6 transition-transform ease-in"}
> >
<span <span class="text-white text-xs font-semibold">
class="text-white text-[0.70rem] font-medium text-opacity-[0.6]"
>
{$etfTicker} {$etfTicker}
</span> </span>
<span class="text-white font-medium text-sm"> <span class="text-white font-medium text-sm">
@ -497,23 +563,19 @@
</div> </div>
<!--End Mobile Navbar--> <!--End Mobile Navbar-->
<div class="pt-14 sm:pt-0 w-full px-3 sm:px-0"> <div class="pt-14 sm:pt-0 w-full px-3 sm:px-0 lg:pr-3">
<div <div
class="md:flex md:justify-between md:divide-x md:divide-slate-800" class="md:flex md:justify-between md:divide-x md:divide-slate-800"
> >
<!-- Main content --> <!-- Main content -->
<div class="pb-12 md:pb-20 w-full"> <div class="pb-12 md:pb-20 w-full">
<div class="md:pr-6 lg:pr-10"> <div class="">
<!-----Start-Header-CandleChart-Indicators------> <!-----Start-Header-CandleChart-Indicators------>
<div <div class="m-auto pl-0 sm:pl-4 overflow-hidden mb-3">
class="m-auto pl-0 sm:pl-4 overflow-hidden mb-3 md:mt-10 xl:pr-7"
>
<div <div
class="hidden sm:flex flex-row w-full justify-between items-center" class="hidden sm:flex flex-row w-full justify-between items-center"
> >
<Markethour />
<!--Start Watchlist--> <!--Start Watchlist-->
{#if data?.user} {#if data?.user}
@ -640,22 +702,135 @@
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-label-has-associated-control --> <!-- svelte-ignore a11y-label-has-associated-control -->
<div class="flex items-center w-full mt-3"> <div class="flex items-center w-full mt-5">
<div <div
class="flex flex-row justify-start w-full items-center" class="flex flex-row justify-start w-full items-center"
> >
<div class="flex flex-col items-start ml-2 sm:ml-3"> <div class="flex flex-col items-start w-full">
<span class="text-md sm:text-lg text-blue-400"> <div
{$etfTicker?.toUpperCase()} class="flex flex-row justify-between items-center w-full sm:-mt-[50px] mb-5 sm:mb-10"
</span>
<span
class="text-xl sm:text-2xl font-semibold sm:font-bold text-white"
> >
{$displayCompanyName?.length > charNumber <div
? $displayCompanyName?.slice(0, charNumber) + class="text-2xl lg:text-3xl font-bold text-white"
"..." >
: $displayCompanyName} {$displayCompanyName?.length > charNumber
</span> ? $displayCompanyName?.slice(0, charNumber) +
"..."
: $displayCompanyName}
<span class="hidden sm:inline-block"
>({$etfTicker?.toUpperCase()})</span
>
</div>
</div>
<div
class="-mt-5 sm:-mt-8 mb-5 flex flex-row items-end space-x-2 xs:space-x-3 sm:space-x-5 text-white"
>
<div class="w-full max-w-[50%] whitespace-nowrap">
<div
class="text-3xl sm:text-4xl font-bold {Object?.keys(
prePostData,
)?.length === 0
? 'inline'
: 'block sm:inline'}"
>
{displayLegend?.close}
</div>
<div
class="font-semibold {Object?.keys(
prePostData,
)?.length === 0
? 'inline'
: 'block sm:inline'} text-lg xs:text-xl sm:text-2xl {displayLegend?.change >=
0
? "before:content-['+'] text-[#00FC50]"
: 'text-[#FF2F1F]'}"
>
{displayLegend?.change}%
</div>
<div class="mt-0.5 text-xs sm:text-sm">
{#if !$isOpen}
<span
class="block font-semibold sm:inline mb-0.5 sm:mb-0"
>At close:</span
>
{/if}
{displayLegend?.date}
{#if $isOpen}
<span
class="{Object?.keys(prePostData)
?.length !== 0
? 'block sm:inline'
: 'inline'} mb-0.5 sm:mb-0"
>- Market open</span
>
{/if}
</div>
</div>
{#if Object?.keys(prePostData)?.length !== 0 && !$isOpen}
<div
class="border-l border-default pl-3 bp:pl-5"
>
<div
class="block text-2xl sm:text-[1.7rem] font-semibold leading-5 text-faded sm:inline"
>
{prePostData?.price?.toFixed(2)}
</div>
<div
class="mt-1.5 block text-sm xs:text-base sm:mt-0 sm:inline sm:text-lg {prePostData?.changesPercentage >=
0
? "before:content-['+'] text-[#00FC50]"
: 'text-[#FF2F1F]'}"
>
{prePostData?.changesPercentage?.toFixed(
2,
)}%
</div>
<div class="mt-1 text-xs sm:text-sm sm:flex">
<span class="flex items-center">
{#if prePostData?.time?.includes("AM")}
<svg
class="h-4 w-4 inline text-yellow-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
style="max-width:40px"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
></path></svg
>
{:else}
<svg
class="h-4 w-4 inline text-blue-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
style="max-width:40px"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
></path></svg
>
{/if}
<span
class="ml-0.5 whitespace-nowrap font-semibold md:ml-1 mb-0.5 sm:mb-0"
>{prePostData?.time?.includes("AM")
? "Pre-market"
: "After-hours"}</span
></span
>
<span class="sm:ml-1 whitespace-nowrap"
>{prePostData?.time}</span
>
</div>
</div>
{/if}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -664,7 +839,6 @@
<!--Start Ticker Section--> <!--Start Ticker Section-->
<!--<div class="w-full max-w-3xl sm:max-w-2xl m-auto pt-2 pb-5 sm:pl-3 sticky z-20 bg-default" style="top: {$screenWidth < 520 && $isScrollingUp ? '4rem' : '0rem'};">-->
<nav <nav
class="sm:ml-4 border-b-[2px] overflow-x-scroll md:overflow-hidden whitespace-nowrap" class="sm:ml-4 border-b-[2px] overflow-x-scroll md:overflow-hidden whitespace-nowrap"
> >
@ -676,28 +850,28 @@
on:click={() => changeSection("overview")} on:click={() => changeSection("overview")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'overview' 'overview'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
Overview Overview
</a> </a>
<a <a
href={`/etf/${$etfTicker}/holdings`} href={`/etf/${$etfTicker}/holdings`}
on:click={() => changeSection("holdings")} on:click={() => changeSection("holdings")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'holdings' 'holdings'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
Holdings Holdings
</a> </a>
<a <a
href={`/etf/${$etfTicker}/options`} href={`/etf/${$etfTicker}/options`}
on:click={() => changeSection("options")} on:click={() => changeSection("options")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'options' 'options'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
Options Options
@ -707,7 +881,7 @@
on:click={() => changeSection("dark-pool")} on:click={() => changeSection("dark-pool")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'dark-pool' 'dark-pool'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
Dark Pool Dark Pool
@ -717,7 +891,7 @@
on:click={() => changeSection("insider")} on:click={() => changeSection("insider")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'insider' 'insider'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
Insider Insider
@ -727,7 +901,7 @@
on:click={() => changeSection("dividends")} on:click={() => changeSection("dividends")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'dividends' 'dividends'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
Dividends Dividends
@ -737,7 +911,7 @@
on:click={() => changeSection("history")} on:click={() => changeSection("history")}
class="p-2 px-5 cursor-pointer {displaySection === class="p-2 px-5 cursor-pointer {displaySection ===
'history' 'history'
? 'text-white bg-secondary sm:hover:bg-opacity-[0.95]' ? 'text-white bg-secondary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}" : 'text-gray-400 sm:hover:text-white sm:hover:bg-secondary sm:hover:bg-opacity-[0.95]'}"
> >
History History
@ -746,6 +920,7 @@
</nav> </nav>
<!--Start-Main Content--> <!--Start-Main Content-->
<slot /> <slot />
<!--End Main Content--> <!--End Main Content-->
</div> </div>
@ -767,6 +942,7 @@
<!--Start SellTrade Modal--> <!--Start SellTrade Modal-->
<PriceAlert {data} ticker={$etfTicker} assetType={$assetType} /> <PriceAlert {data} ticker={$etfTicker} assetType={$assetType} />
<!--Start Add Watchlist Modal--> <!--Start Add Watchlist Modal-->
<input type="checkbox" id="addWatchListModal" class="modal-toggle" /> <input type="checkbox" id="addWatchListModal" class="modal-toggle" />
@ -798,7 +974,7 @@
class="cursor-pointer w-full flex flex-row justify-start items-center mb-5" class="cursor-pointer w-full flex flex-row justify-start items-center mb-5"
> >
<div <div
class="flex flex-row items-center w-full p-3 border rounded-md {item?.ticker?.includes( class="flex flex-row items-center w-full border p-3 rounded-md {item?.ticker?.includes(
$etfTicker, $etfTicker,
) )
? 'border border-gray-400' ? 'border border-gray-400'
@ -814,7 +990,9 @@
</span> </span>
</div> </div>
<div class="rounded-full w-8 h-8 relative border border-gray-600"> <div
class="rounded-full w-8 h-8 relative border border-[#737373]"
>
{#if item?.ticker?.includes($etfTicker)} {#if item?.ticker?.includes($etfTicker)}
<svg <svg
class="w-full h-full rounded-full" class="w-full h-full rounded-full"

View File

@ -1,12 +0,0 @@
import {
etfTicker,
displayCompanyName,
assetType,
} from "$lib/store";
export const load = async ({ data }) => {
etfTicker.set(data?.getParams?.toUpperCase());
assetType.set("etf");
displayCompanyName.set(data?.companyName);
};

View File

@ -6,22 +6,18 @@
setCache, setCache,
numberOfUnreadNotification, numberOfUnreadNotification,
globalForm, globalForm,
isCrosshairMoveActive,
realtimePrice, realtimePrice,
priceIncrease, priceIncrease,
wsBidPrice, wsBidPrice,
wsAskPrice, wsAskPrice,
currentPortfolioPrice, currentPortfolioPrice,
etfTicker, etfTicker,
displayCompanyName,
isOpen,
isBeforeMarketOpen,
isWeekend,
shouldUpdatePriceChart, shouldUpdatePriceChart,
priceChartData, priceChartData,
} from "$lib/store"; } from "$lib/store";
import { onDestroy, onMount } from "svelte"; import { onDestroy, onMount } from "svelte";
import WIIM from "$lib/components/WIIM.svelte"; import WIIM from "$lib/components/WIIM.svelte";
import News from "$lib/components/News.svelte"; import News from "$lib/components/News.svelte";
import ETFSidecard from "$lib/components/ETFSidecard.svelte"; import ETFSidecard from "$lib/components/ETFSidecard.svelte";
@ -30,8 +26,8 @@
export let data; export let data;
export let form; export let form;
let prePostData = {};
let stockDeck = {}; let stockDeck = {};
$: previousClose = data?.getStockQuote?.previousClose; $: previousClose = data?.getStockQuote?.previousClose;
//============================================// //============================================//
const intervals = ["1D", "1W", "1M", "6M", "1Y", "MAX"]; const intervals = ["1D", "1W", "1M", "6M", "1Y", "MAX"];
@ -51,62 +47,71 @@
$: { $: {
if (output !== null) { if (output !== null) {
let change; let change;
let graphChange;
let currentDataRow; let currentDataRow;
let baseClose; let currentDataRowOneDay;
let baseClose = previousClose;
let graphBaseClose;
const length = oneDayPrice?.length;
for (let i = length - 1; i >= 0; i--) {
if (!isNaN(oneDayPrice[i]?.close)) {
currentDataRowOneDay = oneDayPrice[i];
break;
}
}
// Determine current data row and base close price based on displayData // Determine current data row and base close price based on displayData
switch (displayData) { switch (displayData) {
case "1D":
const length = oneDayPrice?.length;
for (let i = length - 1; i >= 0; i--) {
if (!isNaN(oneDayPrice[i]?.close)) {
currentDataRow = oneDayPrice[i];
break;
}
}
baseClose = previousClose;
break;
case "1W": case "1W":
currentDataRow = oneWeekPrice?.at(-1); // Latest entry for 1 week currentDataRow = oneWeekPrice?.at(-1); // Latest entry for 1 week
baseClose = oneWeekPrice?.[0]?.close; graphBaseClose = oneWeekPrice?.at(0)?.close;
break; break;
case "1M": case "1M":
currentDataRow = oneMonthPrice?.at(-1); // Latest entry for 1 month currentDataRow = oneMonthPrice?.at(-1); // Latest entry for 1 month
baseClose = oneMonthPrice?.[0]?.close; graphBaseClose = oneMonthPrice?.at(0)?.close;
break; break;
case "6M": case "6M":
currentDataRow = sixMonthPrice?.at(-1); // Latest entry for 6 months currentDataRow = sixMonthPrice?.at(-1); // Latest entry for 6 months
baseClose = sixMonthPrice?.[0]?.close; graphBaseClose = sixMonthPrice?.at(0)?.close;
break; break;
case "1Y": case "1Y":
currentDataRow = oneYearPrice?.at(-1); // Latest entry for 1 year currentDataRow = oneYearPrice?.at(-1); // Latest entry for 1 year
baseClose = oneYearPrice?.[0]?.close; graphBaseClose = oneYearPrice?.at(0)?.close;
break; break;
case "MAX": case "MAX":
currentDataRow = maxPrice?.at(-1); // Latest entry for MAX range currentDataRow = maxPrice?.at(-1); // Latest entry for MAX range
baseClose = maxPrice?.[0]?.close; graphBaseClose = maxPrice?.at(0)?.close;
break; break;
} }
// Calculate percentage change if baseClose and currentDataRow are valid // Calculate percentage change if baseClose and currentDataRow are valid
const closeValue = const closeValue =
displayData === "1D" && $realtimePrice !== null && $realtimePrice !== undefined
!$isCrosshairMoveActive && ? $realtimePrice
$realtimePrice !== null : (currentDataRowOneDay?.close ?? currentDataRowOneDay?.value);
const graphCloseValue =
$realtimePrice !== null && $realtimePrice !== undefined
? $realtimePrice ? $realtimePrice
: (currentDataRow?.close ?? currentDataRow?.value); : (currentDataRow?.close ?? currentDataRow?.value);
if (closeValue && baseClose) { if (closeValue && baseClose) {
change = ((closeValue / baseClose - 1) * 100).toFixed(2); change = ((closeValue / baseClose - 1) * 100)?.toFixed(2);
}
if (graphCloseValue && graphBaseClose) {
graphChange = ((graphCloseValue / graphBaseClose - 1) * 100)?.toFixed(
2,
);
} }
// Format date // Format date
const date = new Date(currentDataRow?.time * 1000); const date = new Date(currentDataRowOneDay?.time * 1000);
const options = { const options = {
day: "2-digit", day: "2-digit",
@ -117,15 +122,7 @@
timeZone: "UTC", timeZone: "UTC",
}; };
//const formattedDate = (displayData === '1D' || displayData === '1W' || displayData === '1M') ? date.toLocaleString('en-GB', options).replace(/\//g, '.') : date.toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' }).replace(/\//g, '.'); const formattedDate = date?.toLocaleString("en-US", options);
const formattedDate =
displayData === "1D" || displayData === "1W" || displayData === "1M"
? date.toLocaleString("en-US", options)
: new Date(currentDataRow?.time)?.toLocaleDateString("en-US", {
day: "2-digit",
month: "short",
year: "numeric",
});
const safeFormattedDate = const safeFormattedDate =
formattedDate === "Invalid Date" formattedDate === "Invalid Date"
@ -135,11 +132,11 @@
// Set display legend // Set display legend
displayLegend = { displayLegend = {
close: close:
currentDataRow?.value ?? currentDataRowOneDay?.close?.toFixed(2) ??
currentDataRow?.close ?? data?.getStockQuote?.price?.toFixed(2),
data?.getStockQuote?.price,
date: safeFormattedDate, date: safeFormattedDate,
change, change,
graphChange: displayData === "1D" ? change : graphChange,
}; };
} }
} }
@ -413,34 +410,8 @@
} }
} }
async function getPrePostQuote() {
if (!$isOpen) {
const postData = { ticker: $etfTicker, path: "pre-post-quote" };
const response = await fetch("/api/ticker-data", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
prePostData = await response.json();
}
}
let currentDataRow = { value: "-", date: "-" };
let lineLegend = null;
let displayLegend = { close: "-", date: "-" }; let displayLegend = { close: "-", date: "-" };
function handleSeriesReference(ref) {
try {
lineLegend = ref;
} catch (error) {
console.log(error);
}
}
let displayLastLogicalRangeValue; let displayLastLogicalRangeValue;
const fitContentChart = async () => { const fitContentChart = async () => {
@ -686,19 +657,11 @@
oneMonthPrice = []; oneMonthPrice = [];
oneYearPrice = []; oneYearPrice = [];
maxPrice = []; maxPrice = [];
prePostData = {};
output = null; output = null;
stockDeck = data?.getETFProfile?.at(0); // Essential otherwise chart will not be updated since we wait until #layout.server.ts server response is finished stockDeck = data?.getETFProfile?.at(0); // Essential otherwise chart will not be updated since we wait until #layout.server.ts server response is finished
const asyncFunctions = [getPrePostQuote()];
Promise.all(asyncFunctions) initializePrice();
.then((results) => {
initializePrice();
})
.catch((error) => {
console.error("An error occurred:", error);
});
} }
} }
</script> </script>
@ -708,21 +671,21 @@
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title> <title>
{$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""}
{$displayCompanyName} ({$etfTicker}) Stock Price, Quote & News · Stocknear {data?.companyName} ({$etfTicker}) Stock Price, Quote & News · Stocknear
</title> </title>
<meta <meta
name="description" name="description"
content={`Get a real-time ${$displayCompanyName} (${$etfTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`} content={`Get a real-time ${data?.companyName} (${$etfTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`}
/> />
<!-- Other meta tags --> <!-- Other meta tags -->
<meta <meta
property="og:title" property="og:title"
content={`${$displayCompanyName} (${$etfTicker}) Stock Price, Quote & News · Stocknear`} content={`${data?.companyName} (${$etfTicker}) Stock Price, Quote & News · Stocknear`}
/> />
<meta <meta
property="og:description" property="og:description"
content={`Get a real-time ${$displayCompanyName} (${$etfTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`} content={`Get a real-time ${data?.companyName} (${$etfTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`}
/> />
<!--<meta property="og:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>--> <!--<meta property="og:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>-->
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -732,11 +695,11 @@
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta <meta
name="twitter:title" name="twitter:title"
content={`${$displayCompanyName} (${$etfTicker}) Stock Price, Quote & News · Stocknear`} content={`${data?.companyName} (${$etfTicker}) Stock Price, Quote & News · Stocknear`}
/> />
<meta <meta
name="twitter:description" name="twitter:description"
content={`Get a real-time ${$displayCompanyName} (${$etfTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`} content={`Get a real-time ${data?.companyName} (${$etfTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`}
/> />
<!--<meta name="twitter:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>--> <!--<meta name="twitter:image" content="https://stocknear-pocketbase.s3.amazonaws.com/logo/meta_logo.jpg"/>-->
<!-- Add more Twitter meta tags as needed --> <!-- Add more Twitter meta tags as needed -->
@ -749,100 +712,7 @@
> >
<!-- Main content --> <!-- Main content -->
<div class="pb-12 md:pb-20 w-full sm:pr-6 xl:pr-0"> <div class="pb-12 md:pb-20 w-full sm:pr-6 xl:pr-0">
<div class="xl:pr-10"> <div class="mt-2">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-label-has-associated-control -->
<div class="flex flex-row items-start w-full sm:pl-6 mt-4">
<div class="flex flex-col items-start justify-start w-full">
<div
class="text-2xl md:text-3xl font-bold text-white flex flex-row items-center w-full"
>
{$realtimePrice ?? displayLegend?.close}
{#if $priceIncrease === true}
<div
style="background-color: green;"
class="inline-block pulse rounded-full w-3 h-3 ml-2"
></div>
{:else if $priceIncrease === false}
<div
style="background-color: red;"
class="inline-block pulse rounded-full w-3 h-3 ml-2"
></div>
{/if}
</div>
<div class="flex flex-row items-center w-full">
<span
class="items-center justify-start {displayLegend?.change > 0
? "before:content-['+'] text-[#00FC50]"
: 'text-[#FF2F1F]'} font-medium text-xs sm:text-sm"
>{displayLegend?.change ?? "-"}%</span
>
<span class="ml-3 text-white text-xs sm:text-sm"
>{displayLegend?.date}</span
>
</div>
</div>
<div class="ml-auto">
{#if Object?.keys(prePostData)?.length !== 0 && prePostData?.price !== 0}
<div class="flex flex-col justify-end items-end">
<div class="flex flex-row items-center justify-end">
<span class="text-white text-lg sm:text-2xl font-bold">
{prePostData?.price}
</span>
{#if prePostData?.changesPercentage >= 0}
<span
class="ml-1 items-center justify-start text-[#00FC50] font-medium text-xs sm:text-sm"
>({prePostData?.changesPercentage}%)</span
>
{:else if prePostData?.changesPercentage < 0}
<span
class="ml-1 items-center justify-start text-[#FF2F1F] font-medium text-xs sm:text-sm"
>({prePostData?.changesPercentage}%)</span
>
{/if}
</div>
{#if $isBeforeMarketOpen && !$isOpen && !$isWeekend}
<div
class="flex flex-row items-center justify-end text-white text-xs sm:text-sm font-normal text-end w-24"
>
<span>Pre-market:</span>
<svg
class="ml-1 w-4 h-4 inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
><path
fill="#EA9703"
d="M120 40V16a8 8 0 0 1 16 0v24a8 8 0 0 1-16 0m72 88a64 64 0 1 1-64-64a64.07 64.07 0 0 1 64 64m-16 0a48 48 0 1 0-48 48a48.05 48.05 0 0 0 48-48M58.34 69.66a8 8 0 0 0 11.32-11.32l-16-16a8 8 0 0 0-11.32 11.32Zm0 116.68l-16 16a8 8 0 0 0 11.32 11.32l16-16a8 8 0 0 0-11.32-11.32M192 72a8 8 0 0 0 5.66-2.34l16-16a8 8 0 0 0-11.32-11.32l-16 16A8 8 0 0 0 192 72m5.66 114.34a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32-11.32ZM48 128a8 8 0 0 0-8-8H16a8 8 0 0 0 0 16h24a8 8 0 0 0 8-8m80 80a8 8 0 0 0-8 8v24a8 8 0 0 0 16 0v-24a8 8 0 0 0-8-8m112-88h-24a8 8 0 0 0 0 16h24a8 8 0 0 0 0-16"
/></svg
>
</div>
{:else}
<div
class="flex flex-row items-center justify-end text-white text-xs sm:text-sm font-normal text-end w-28"
>
<span>After-hours:</span>
<svg
class="ml-1 w-4 h-4 inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
><path
fill="#70A1EF"
d="M232.13 143.64a6 6 0 0 0-6-1.49a90.07 90.07 0 0 1-112.27-112.3a6 6 0 0 0-7.49-7.48a102.88 102.88 0 0 0-51.89 36.31a102 102 0 0 0 142.84 142.84a102.88 102.88 0 0 0 36.31-51.89a6 6 0 0 0-1.5-5.99m-42 48.29a90 90 0 0 1-126-126a90.9 90.9 0 0 1 35.52-28.27a102.06 102.06 0 0 0 118.69 118.69a90.9 90.9 0 0 1-28.24 35.58Z"
/></svg
>
</div>
{/if}
</div>
{/if}
</div>
</div>
<!-----End-Header-CandleChart-Indicators------>
<!--End Ticker Section--> <!--End Ticker Section-->
<!-- Start Graph --> <!-- Start Graph -->
@ -877,20 +747,6 @@
{/each} {/each}
</ul> </ul>
</div> </div>
<div
class="flex shrink flex-row space-x-1 pr-1 text-sm sm:text-[1rem]"
>
<span
class={displayLegend?.change >= 0
? "before:content-['+'] text-[#00FC50]"
: "text-[#FF2F1F]"}
>
{displayLegend?.change ?? "-"}%
</span>
<span class="hidden text-gray-200 sm:block"
>({displayData})</span
>
</div>
</div> </div>
<div class="h-[250px] sm:h-[350px]"> <div class="h-[250px] sm:h-[350px]">
<div <div
@ -938,11 +794,11 @@
class="flex shrink flex-row space-x-1 pr-1 text-sm sm:text-[1rem]" class="flex shrink flex-row space-x-1 pr-1 text-sm sm:text-[1rem]"
> >
<span <span
class={displayLegend?.change >= 0 class={displayLegend?.graphChange >= 0
? "before:content-['+'] text-[#00FC50]" ? "before:content-['+'] text-[#00FC50]"
: "text-[#FF2F1F]"} : "text-[#FF2F1F]"}
> >
{displayLegend?.change}% {displayLegend?.graphChange}%
</span> </span>
<span class="hidden text-gray-200 sm:block" <span class="hidden text-gray-200 sm:block"
>({displayData})</span >({displayData})</span
@ -968,7 +824,6 @@
lineColor={colorChange} lineColor={colorChange}
topColor={topColorChange} topColor={topColorChange}
bottomColor={bottomColorChange} bottomColor={bottomColorChange}
ref={handleSeriesReference}
priceLineVisible={false} priceLineVisible={false}
> >
<PriceLine <PriceLine
@ -988,7 +843,6 @@
lineColor={colorChange} lineColor={colorChange}
topColor={topColorChange} topColor={topColorChange}
bottomColor={bottomColorChange} bottomColor={bottomColorChange}
ref={handleSeriesReference}
priceLineVisible={false} priceLineVisible={false}
> >
<PriceLine <PriceLine
@ -1008,7 +862,6 @@
lineColor={colorChange} lineColor={colorChange}
topColor={topColorChange} topColor={topColorChange}
bottomColor={bottomColorChange} bottomColor={bottomColorChange}
ref={handleSeriesReference}
priceLineVisible={false} priceLineVisible={false}
> >
<PriceLine <PriceLine
@ -1028,7 +881,6 @@
lineColor={colorChange} lineColor={colorChange}
topColor={topColorChange} topColor={topColorChange}
bottomColor={bottomColorChange} bottomColor={bottomColorChange}
ref={handleSeriesReference}
priceLineVisible={false} priceLineVisible={false}
> >
<PriceLine <PriceLine
@ -1048,7 +900,6 @@
lineColor={colorChange} lineColor={colorChange}
topColor={topColorChange} topColor={topColorChange}
bottomColor={bottomColorChange} bottomColor={bottomColorChange}
ref={handleSeriesReference}
priceLineVisible={false} priceLineVisible={false}
> >
<PriceLine <PriceLine
@ -1068,7 +919,6 @@
lineColor={colorChange} lineColor={colorChange}
topColor={topColorChange} topColor={topColorChange}
bottomColor={bottomColorChange} bottomColor={bottomColorChange}
ref={handleSeriesReference}
priceLineVisible={false} priceLineVisible={false}
> >
<PriceLine <PriceLine
@ -1101,7 +951,7 @@
class="mt-10 lg:mt-0 order-5 lg:order-1 flex flex-row space-x-2 sm:space-x-3 xs:space-x-4" class="mt-10 lg:mt-0 order-5 lg:order-1 flex flex-row space-x-2 sm:space-x-3 xs:space-x-4"
> >
<table <table
class="w-[50%] text-sm text-white tiny:text-small lg:w-full lg:min-w-[210px]" class="w-[50%] text-sm text-white sm:text-[1rem] lg:min-w-[250px] 2xl:min-w-[300px]"
> >
<tbody <tbody
><tr ><tr
@ -1221,8 +1071,7 @@
</tbody> </tbody>
</table> </table>
<table <table
class="w-[48%] text-sm text-white tiny:text-small lg:w-auto lg:min-w-[210px]" class="w-[50%] text-sm text-white lg:min-w-[250px] 2xl:min-w-[300px]"
data-test="overview-quote"
> >
<tbody <tbody
><tr ><tr
@ -1332,16 +1181,15 @@
<!--End Graph--> <!--End Graph-->
<div <div
class="mt-6 flex flex-col lg:flex-row gap-x-14 items-start w-full" class="mt-6 flex flex-col lg:flex-row gap-x-14 items-start w-full justify-between"
> >
<div <div
class="lg:space-y-6 lg:order-2 lg:pt-1 sm:pl-7 lg:pl-0 w-full lg:w-[45%] sm:ml-auto" class="lg:space-y-6 lg:order-2 lg:pt-1 sm:pl-7 lg:pl-0 w-full lg:w-[45%] sm:ml-auto lg:max-w-[400px]"
> >
<ETFSidecard {data} /> <ETFSidecard {data} />
<div class="lg:sticky lg:top-20"></div>
</div> </div>
<div class="w-full"> <div class="w-full lg:w-[65%] 2xl:w-[70%]">
<div <div
class="w-full mt-10 sm:mt-0 m-auto sm:pl-6 sm:pb-6 {data class="w-full mt-10 sm:mt-0 m-auto sm:pl-6 sm:pb-6 {data
?.getWhyPriceMoved?.length !== 0 ?.getWhyPriceMoved?.length !== 0
@ -1351,7 +1199,7 @@
<WIIM {data} /> <WIIM {data} />
</div> </div>
<div class="w-full mt-10 sm:mt-0 m-auto sm:pl-6 sm:pb-6 sm:pt-6"> <div class="w-full mt-5 sm:mt-0 m-auto sm:pl-6 sm:pb-6">
<News {data} /> <News {data} />
</div> </div>
</div> </div>

View File

@ -254,11 +254,7 @@
}); });
$: { $: {
if ( if ($stockTicker && $stockTicker?.length !== 0) {
$stockTicker &&
$stockTicker?.length !== 0 &&
typeof window !== "undefined"
) {
// add a check to see if running on client-side // add a check to see if running on client-side
$stockTicker = data?.getParams; $stockTicker = data?.getParams;
$assetType = "stock"; $assetType = "stock";
@ -337,17 +333,13 @@
$: charNumber = $screenWidth < 640 ? 25 : 40; $: charNumber = $screenWidth < 640 ? 25 : 40;
$: { $: {
if ( if ($stockTicker && $page.url.pathname === `/stocks/${$stockTicker}`) {
$stockTicker &&
typeof window !== "undefined" &&
$page.url.pathname === `/stocks/${$stockTicker}`
) {
displaySection = "overview"; displaySection = "overview";
} }
} }
$: { $: {
if ($page?.url?.pathname && typeof window !== "undefined") { if ($page?.url?.pathname) {
const parts = $page?.url?.pathname?.split("/"); const parts = $page?.url?.pathname?.split("/");
const sectionMap = { const sectionMap = {
statistics: "statistics", statistics: "statistics",

View File

@ -1230,7 +1230,7 @@
<WIIM {data} /> <WIIM {data} />
</div> </div>
<div class="w-full mt-5 sm:mt-0 m-auto sm:pl-6 sm:pb-6 sm:pt-6"> <div class="w-full mt-5 sm:mt-0 m-auto sm:pl-6 sm:pb-6">
<News {data} /> <News {data} />
</div> </div>
</div> </div>