refactor analyst page

This commit is contained in:
MuslemRahimi 2024-10-31 13:50:19 +01:00
parent f85a0e21d6
commit fc64088ee4
2 changed files with 350 additions and 344 deletions

View File

@ -117,6 +117,91 @@
> >
<div class="sm:p-7 w-full m-auto mt-2 sm:mt-0"> <div class="sm:p-7 w-full m-auto mt-2 sm:mt-0">
<div class="w-full mb-6"> <div class="w-full mb-6">
<div
class="rounded-sm border border-gray-600 p-0.5 xs:p-1 md:flex md:flex-col md:space-y-4 md:divide-y md:p-4 lg:flex-row lg:space-x-4 lg:space-y-0 lg:divide-x lg:divide-y-0"
>
<div
class="p-3 md:flex md:space-x-4 md:p-0 lg:block lg:max-w-[32%] lg:space-x-0"
>
<div data-test="forecast-snippet">
<div class="flex items-baseline justify-between">
<h2 class="mb-1 text-xl font-bold">Stock Price Forecast</h2>
<span></span>
</div>
<p>
The 15 analysts with 12-month price forecasts for SMCI stock
have an average target of 74.53, with a low estimate of 32.5
and a high estimate of 135. The average target predicts an
increase of 125.37% from the current stock price of 33.07.
</p>
</div>
<div>
<div class="h-[160px]">
<canvas
id="myChart"
style="display: block; box-sizing: border-box; height: 160px; width: 352px;"
width="529"
height="240"
></canvas>
</div>
<div class="-mt-2 text-center text-xl font-semibold">
Analyst Consensus: <span class="font-bold text-green-700"
>Buy</span
>
</div>
</div>
</div>
<div class="grow pt-2 md:pt-4 lg:pl-4 lg:pt-0">
<div class="h-[250px] xs:h-[275px]">
<canvas
id="myChart"
style="display: block; box-sizing: border-box; height: 275px; width: 728px;"
width="1092"
height="412"
></canvas>
</div>
<div
class="hide-scroll mb-1 mt-2 overflow-x-auto px-1.5 text-center md:mb-0 md:px-0 lg:mt-2"
data-test="forecast-target-table"
>
<table
class="w-full text-right text-tiny text-white xs:text-sm sm:text-base"
>
<thead
><tr class="border-b border-gray-600 font-normal"
><th class="py-[3px] text-left font-semibold lg:py-0.5"
>Target</th
> <th class="font-semibold">Low</th>
<th class="font-semibold">Average</th>
<th class="font-semibold">Median</th>
<th class="font-semibold">High</th></tr
></thead
>
<tbody
><tr class="border-b border-gray-600 font-normal"
><td class="py-[3px] text-left lg:py-0.5">Price</td>
<td>$32.5</td> <td>$74.53</td> <td>$67.5</td>
<td>$135</td></tr
>
<tr
><td class="py-[3px] text-left lg:py-0.5">Change</td>
<td class="text-red-700 dark:text-red-default">-1.72%</td>
<td class="text-green-800 dark:text-green-default"
>+125.37%</td
>
<td class="text-green-800 dark:text-green-default"
>+104.11%</td
>
<td class="text-green-800 dark:text-green-default"
>+308.22%</td
></tr
></tbody
>
</table>
</div>
</div>
</div>
<h2 class="mt-5 text-xl sm:text-2xl text-gray-200 font-bold mb-4"> <h2 class="mt-5 text-xl sm:text-2xl text-gray-200 font-bold mb-4">
Financial Forecast this Year Financial Forecast this Year
</h2> </h2>

View File

@ -3,7 +3,6 @@
numberOfUnreadNotification, numberOfUnreadNotification,
displayCompanyName, displayCompanyName,
stockTicker, stockTicker,
currentPortfolioPrice,
} from "$lib/store"; } from "$lib/store";
import InfoModal from "$lib/components/InfoModal.svelte"; import InfoModal from "$lib/components/InfoModal.svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
@ -19,7 +18,6 @@
let numOfAnalyst = 0; let numOfAnalyst = 0;
let consensusRating = "n/a"; let consensusRating = "n/a";
let changesPercentage = 0; let changesPercentage = 0;
let isLoaded = false;
const tabs = [ const tabs = [
{ {
@ -44,7 +42,7 @@
consensusRating = analystRating?.consensusRating; consensusRating = analystRating?.consensusRating;
changesPercentage = changesPercentage =
analystRating?.priceTarget !== ("n/a" && 0) analystRating?.priceTarget !== ("n/a" && 0)
? ((priceTarget / $currentPortfolioPrice - 1) * 100)?.toFixed(2) ? ((priceTarget / data?.getStockQuote?.price - 1) * 100)?.toFixed(2)
: "-"; : "-";
} }
if (activeIdx === 1) { if (activeIdx === 1) {
@ -87,11 +85,9 @@
return differenceInDays <= 4; return differenceInDays <= 4;
} }
onMount(async () => {
isLoaded = false;
changeTab(0); changeTab(0);
isLoaded = true;
onMount(() => {
window.addEventListener("scroll", handleScroll); window.addEventListener("scroll", handleScroll);
return () => { return () => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
@ -145,143 +141,37 @@
> >
<div class="sm:p-7 w-full m-auto mt-2 sm:mt-0"> <div class="sm:p-7 w-full m-auto mt-2 sm:mt-0">
<div class="mb-6"> <div class="mb-6">
<h2 class="text-2xl sm:text-3xl text-gray-200 font-bold mb-4">
Analyst Ratings
</h2>
</div>
{#if isLoaded}
<div <div
class="mb-4 grid grid-cols-2 grid-rows-2 divide-gray-500 rounded-lg border border-gray-600 bg-[#272727] shadow md:grid-cols-4 md:grid-rows-1 md:divide-x" class="mb-5 flex flex-col justify-between gap-y-2.5 sm:mb-2 sm:flex-row sm:items-end"
> >
<div class="p-4 bp:p-5 sm:p-6"> <h1 class="mb-px text-xl font-bold bp:text-2xl sm:pl-1">
<label {$displayCompanyName} Analyst Ratings
for="totalAnalystInfo" </h1>
class="mr-1 cursor-pointer flex flex-row items-center text-white text-[1rem]" <div>
> <div class="pr-4 hidden justify-end md:flex">
Total Analysts
<InfoModal
title={"Total Analyst"}
content={"The total number of analyst who provided a rating in the past 12 months."}
id={"totalAnalystInfo"}
/>
</label>
<div
class="mt-1 break-words font-semibold leading-8 text-light text-xl"
>
{numOfAnalyst}
</div>
</div>
<div class="p-4 bp:p-5 sm:p-6 border-l border-contrast md:border-0">
<label
for="consensusRatingInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-[1rem]"
>
Total Rating
<InfoModal
title={"Consensus Rating"}
content={`The average analyst rating for ${$stockTicker} is standardized to align with categories: Strong Buy, Buy, Hold, Sell, and Strong Sell.`}
id={"consensusRatingInfo"}
/>
</label>
<div
class="mt-1 break-words font-semibold leading-8 text-light text-xl"
>
{consensusRating}
</div>
</div>
<div class="p-4 bp:p-5 sm:p-6 border-t border-contrast md:border-0">
<label
for="priceTargetInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-[1rem]"
>
Price Target
<InfoModal
title={"Price Target"}
content={"The average 12-month price target"}
id={"priceTargetInfo"}
/>
</label>
<div
class="mt-1 break-words font-semibold leading-8 text-light text-xl"
>
{priceTarget}
</div>
</div>
<div
class="p-4 bp:p-5 sm:p-6 border-t border-contrast md:border-0 border-l border-contrast md:border-0"
>
<label
for="upsideInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-[1rem]"
>
Upside [%]
<InfoModal
title={"Upside"}
content={"The average price target's percentage difference from the current stock price."}
id={"upsideInfo"}
/>
</label>
<div
class="mt-1 break-words font-semibold leading-8 text-light text-xl {changesPercentage >=
0
? 'text-[#00FC50]'
: changesPercentage < 0
? 'text-[#FF2F1F]'
: 'text-white'}"
>
{changesPercentage > 0 ? "+" : ""}{changesPercentage}
</div>
</div>
</div>
<h3
class="mt-10 text-2xl sm:text-3xl text-gray-200 font-bold mb-4 text-center sm:text-start"
>
Ratings History
</h3>
<div
class="mt-5 w-fit text-white p-3 sm:p-5 mb-5 rounded-lg sm:flex sm:flex-row sm:items-center border border-slate-800 text-sm sm:text-[1rem]"
>
<svg
class="w-6 h-6 flex-shrink-0 inline-block sm:mr-2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
><path
fill="#a474f6"
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m-4 48a12 12 0 1 1-12 12a12 12 0 0 1 12-12m12 112a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 0 16"
/></svg
>
In previous years, Wall Street analysts have given {$displayCompanyName}
a total of {data?.getAnalystTickerHistory?.length} ratings.
</div>
<div class="flex flex-col items-end w-full pt-10">
<InfoModal <InfoModal
title={"Top Analysts"} title={"Top Analysts"}
content={"Filter for analysts rated 4+ stars focusing on their win rate and average return per rating. Analysts with 4+ stars typically exhibit both high accuracy and high return per rating."} content={"Filter for analysts rated 4+ stars focusing on their win rate and average return per rating. Analysts with 4+ stars typically exhibit both high accuracy and high return per rating."}
id={"topAnalystsInfo"} id={"topAnalystsInfo"}
/> />
</div>
<div class="ml-auto">
<div <div
class="bg-[#313131] w-fit relative flex flex-wrap items-center justify-center rounded-lg p-1 mt-4" class="inline-flex justify-center w-full rounded-md shadow-sm sm:w-auto"
>
<div
class="bg-[#313131] w-fit relative flex flex-wrap items-center justify-center rounded-md p-1 mt-4"
> >
{#each tabs as item, i} {#each tabs as item, i}
<button <button
on:click={() => changeTab(i)} on:click={() => changeTab(i)}
class="group relative z-[1] rounded-full px-6 py-2 {activeIdx === class="group relative z-[1] rounded-full px-5 py-1 {activeIdx ===
i i
? 'z-0' ? 'z-0'
: ''} " : ''} "
> >
{#if activeIdx === i} {#if activeIdx === i}
<div <div
class="absolute inset-0 rounded-lg bg-purple-600" class="absolute inset-0 rounded-md bg-purple-600"
></div> ></div>
{/if} {/if}
<span <span
@ -294,6 +184,58 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
<div
class="mb-4 grid grid-cols-2 grid-rows-2 divide-contrast rounded-lg border border-gray-600 md:grid-cols-4 md:grid-rows-1 md:divide-x"
>
<div class="p-4 bp:p-5 sm:p-6">
<div class="text-sm font-normal text-default xs:text-base">
Total Analysts
</div>
<div
class="mt-1 break-words font-semibold leading-8 text-white tiny:text-lg xs:text-xl sm:text-2xl"
>
{numOfAnalyst}
</div>
</div>
<div class="p-4 bp:p-5 sm:p-6 border-l border-gray-600 md:border-0">
<div class="text-sm font-normal text-default xs:text-base">
Consensus Rating
</div>
<div
class="mt-1 break-words font-semibold leading-8 text-white tiny:text-lg xs:text-xl sm:text-2xl"
>
{consensusRating}
</div>
</div>
<div class="p-4 bp:p-5 sm:p-6 border-t border-gray-600 md:border-0">
<div class="text-sm font-normal text-default xs:text-base">
Price Target
</div>
<div
class="mt-1 break-words font-semibold leading-8 text-white tiny:text-lg xs:text-xl sm:text-2xl"
>
${priceTarget}
</div>
</div>
<div
class="p-4 bp:p-5 sm:p-6 border-t border-gray-600 md:border-0 border-l border-gray-600 md:border-0"
>
<div class="text-sm font-normal text-default xs:text-base">
Upside
</div>
<div
class="mt-1 break-words font-semibold leading-8 tiny:text-lg xs:text-xl sm:text-2xl {changesPercentage >=
0
? "before:content-['+'] text-[#00FC50]"
: 'text-[#FF2F1F]'}"
>
{changesPercentage}%
</div>
</div>
</div>
{#if rawData?.length !== 0} {#if rawData?.length !== 0}
<div class="sm:w-full m-auto mt-10"> <div class="sm:w-full m-auto mt-10">
@ -305,12 +247,10 @@
> >
<thead class=""> <thead class="">
<tr class="border-b border-[#27272A]"> <tr class="border-b border-[#27272A]">
<td <td class="text-white font-semibold text-[1rem] text-start"
class="text-white font-semibold text-[1rem] text-start"
>Analyst</td >Analyst</td
> >
<td <td class="text-white font-semibold text-[1rem] text-start"
class="text-white font-semibold text-[1rem] text-start"
>Rating</td >Rating</td
> >
<td class="text-white font-semibold text-[1rem] text-end" <td class="text-white font-semibold text-[1rem] text-end"
@ -323,8 +263,7 @@
<tr <tr
class="{latestInfoDate(item?.date) class="{latestInfoDate(item?.date)
? 'bg-[#F9AB00] bg-opacity-[0.1]' ? 'bg-[#F9AB00] bg-opacity-[0.1]'
: 'odd:bg-[#27272A]'} border-b-[#09090B] {index + : 'odd:bg-[#27272A]'} border-b-[#09090B] {index + 1 ===
1 ===
historyList?.slice(0, 3)?.length && historyList?.slice(0, 3)?.length &&
data?.user?.tier !== 'Pro' data?.user?.tier !== 'Pro'
? 'opacity-[0.1]' ? 'opacity-[0.1]'
@ -430,15 +369,11 @@
/></svg /></svg
> >
<span class="text-white font-semibold" <span class="text-white font-semibold"
>${Math?.ceil( >${Math?.ceil(item?.adjusted_pt_current)}</span
item?.adjusted_pt_current,
)}</span
> >
{:else if Math?.ceil(item?.adjusted_pt_current) !== 0} {:else if Math?.ceil(item?.adjusted_pt_current) !== 0}
<span class="text-white font-semibold" <span class="text-white font-semibold"
>${Math?.ceil( >${Math?.ceil(item?.adjusted_pt_current)}</span
item?.adjusted_pt_current,
)}</span
> >
{/if} {/if}
</div> </div>
@ -471,9 +406,7 @@
</div> </div>
</div> </div>
{:else if activeIdx === 1} {:else if activeIdx === 1}
<div <div class="w-full flex justify-start items-center m-auto mt-10 mb-6">
class="w-full flex justify-start items-center m-auto mt-10 mb-6"
>
<div <div
class="text-center w-fit text-gray-100 text-sm sm:text-[1rem] rounded-lg h-auto border border-slate-800 p-4" class="text-center w-fit text-gray-100 text-sm sm:text-[1rem] rounded-lg h-auto border border-slate-800 p-4"
> >
@ -486,8 +419,8 @@
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m-4 48a12 12 0 1 1-12 12a12 12 0 0 1 12-12m12 112a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 0 16" d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m-4 48a12 12 0 1 1-12 12a12 12 0 0 1 12-12m12 112a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 0 16"
/></svg /></svg
> >
There are no top analyst ratings available for the past 12 months There are no top analyst ratings available for the past 12 months for
for {$displayCompanyName} {$displayCompanyName}
</div> </div>
</div> </div>
{/if} {/if}
@ -498,18 +431,6 @@
title="Get stock forecasts from Wall Street's highest rated professionals" title="Get stock forecasts from Wall Street's highest rated professionals"
/> />
{/if} {/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</div> </div>
</div> </div>
</div> </div>