bugfixing websocket
This commit is contained in:
parent
36b7a25e90
commit
681d9cc423
@ -1,21 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { analystEstimateComponent, stockTicker } from "$lib/store";
|
import { analystEstimateComponent, stockTicker } from "$lib/store";
|
||||||
|
|
||||||
import { Chart } from "svelte-echarts";
|
|
||||||
import { init, use } from "echarts/core";
|
|
||||||
import { LineChart, CustomChart } from "echarts/charts";
|
|
||||||
import { GridComponent, TooltipComponent } from "echarts/components";
|
|
||||||
import { CanvasRenderer } from "echarts/renderers";
|
|
||||||
import { abbreviateNumber } from "$lib/utils";
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
|
import EstimationGraph from "$lib/components/EstimationGraph.svelte";
|
||||||
|
import Lazy from "svelte-lazy";
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
use([
|
|
||||||
LineChart,
|
|
||||||
CustomChart,
|
|
||||||
GridComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
CanvasRenderer,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let analystEstimateList = [];
|
let analystEstimateList = [];
|
||||||
let isLoaded = false;
|
let isLoaded = false;
|
||||||
@ -829,530 +818,53 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="space-y-6 lg:grid lg:grid-cols-2 lg:gap-6 lg:space-y-0 mt-10">
|
<div class="space-y-6 lg:grid lg:grid-cols-2 lg:gap-6 lg:space-y-0 mt-10">
|
||||||
<div>
|
<Lazy>
|
||||||
<h2 class="mb-2 text-xl font-bold">Revenue Forecast</h2>
|
<EstimationGraph
|
||||||
<div class="rounded-sm border p-2 border-gray-600">
|
userTier={data?.user?.tier}
|
||||||
<div class="app h-[275px] w-full">
|
title="Revenue"
|
||||||
{#if optionsRevenue !== null}
|
options={optionsRevenue}
|
||||||
<Chart {init} options={optionsRevenue} class="chart" />
|
tableDataList={revenueDateList}
|
||||||
{/if}
|
highDataList={highRevenueList}
|
||||||
</div>
|
avgDataList={avgRevenueList}
|
||||||
<div
|
lowDataList={lowRevenueList}
|
||||||
class="mt-3 overflow-x-auto p-0 text-center sm:p-0.5 lg:mt-3.5"
|
/>
|
||||||
>
|
</Lazy>
|
||||||
<table class="w-full text-right">
|
|
||||||
<thead
|
|
||||||
><tr
|
|
||||||
class="border-b border-gray-600 align-bottom text-white font-normal"
|
|
||||||
><th
|
|
||||||
class="p-1 text-left font-semibold text-sm sm:text-[1rem]"
|
|
||||||
>Revenue</th
|
|
||||||
>
|
|
||||||
{#each revenueDateList as date, index}
|
|
||||||
<th class="p-1 font-semibold text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}{date}{/if}</th
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</tr></thead
|
|
||||||
>
|
|
||||||
<tbody
|
|
||||||
><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>High</td
|
|
||||||
>
|
|
||||||
{#each highRevenueList as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= highRevenueList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
{abbreviateNumber(item?.val)}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Avg</td
|
|
||||||
>
|
|
||||||
{#each avgRevenueList as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= avgRevenueList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
{abbreviateNumber(item?.val)}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Low</td
|
|
||||||
>
|
|
||||||
{#each lowRevenueList as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= lowRevenueList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
{abbreviateNumber(item?.val)}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr></tbody
|
|
||||||
>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<Lazy>
|
||||||
<h2 class="mb-2 text-xl font-bold">Revenue Growth</h2>
|
<EstimationGraph
|
||||||
<div class="rounded-sm border p-2 border-gray-600">
|
userTier={data?.user?.tier}
|
||||||
<div class="app h-[275px] w-full">
|
title="Revenue Growth"
|
||||||
{#if optionsRevenueGrowth !== null}
|
options={optionsRevenueGrowth}
|
||||||
<Chart {init} options={optionsRevenueGrowth} class="chart" />
|
tableDataList={revenueDateList}
|
||||||
{/if}
|
highDataList={highRevenueList}
|
||||||
</div>
|
avgDataList={avgRevenueList}
|
||||||
<div
|
lowDataList={lowRevenueList}
|
||||||
class="mt-3 overflow-x-auto p-0 text-center sm:p-0.5 lg:mt-3.5"
|
/>
|
||||||
>
|
</Lazy>
|
||||||
<table class="w-full text-right">
|
|
||||||
<thead
|
|
||||||
><tr
|
|
||||||
class="border-b border-gray-600 align-bottom text-white font-normal whitespace-nowrap"
|
|
||||||
><th
|
|
||||||
class="p-1 text-left font-semibold text-sm sm:text-[1rem]"
|
|
||||||
>Revenue Growth</th
|
|
||||||
>
|
|
||||||
{#each revenueDateList as date, index}
|
|
||||||
<th class="p-1 font-semibold text-sm sm:text-[1rem]"
|
|
||||||
>{#if index !== 0}{date}{/if}</th
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</tr></thead
|
|
||||||
>
|
|
||||||
<tbody
|
|
||||||
><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>High</td
|
|
||||||
>
|
|
||||||
{#each computeGrowthSingleList(highRevenueList, avgRevenueList) as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= highRevenueList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
<span
|
|
||||||
class={item?.growth !== null && item?.growth > 0
|
|
||||||
? "text-[#00FC50] before:content-['+']"
|
|
||||||
: item?.growth < 0
|
|
||||||
? "text-[#FF2F1F]"
|
|
||||||
: "text-white"}
|
|
||||||
>
|
|
||||||
{item?.growth !== null &&
|
|
||||||
Math.abs(item?.growth - 0) > 0
|
|
||||||
? abbreviateNumber(item?.growth) + "%"
|
|
||||||
: "-"}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Avg</td
|
|
||||||
>
|
|
||||||
{#each revenueAvgGrowthList?.filter((item) => item.FY >= 24) as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= avgRevenueList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
<span
|
|
||||||
class={item?.growth !== null && item?.growth > 0
|
|
||||||
? "text-[#00FC50] before:content-['+']"
|
|
||||||
: item?.growth < 0
|
|
||||||
? "text-[#FF2F1F]"
|
|
||||||
: "text-white"}
|
|
||||||
>
|
|
||||||
{item?.growth !== null &&
|
|
||||||
Math.abs(item?.growth - 0) > 0
|
|
||||||
? abbreviateNumber(item?.growth) + "%"
|
|
||||||
: "-"}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Low</td
|
|
||||||
>
|
|
||||||
{#each computeGrowthSingleList(lowRevenueList, avgRevenueList) as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= lowRevenueList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
<span
|
|
||||||
class={item?.growth !== null && item?.growth > 0
|
|
||||||
? "text-[#00FC50] before:content-['+']"
|
|
||||||
: item?.growth < 0
|
|
||||||
? "text-[#FF2F1F]"
|
|
||||||
: "text-white"}
|
|
||||||
>
|
|
||||||
{item?.growth !== null &&
|
|
||||||
Math.abs(item?.growth - 0) > 0
|
|
||||||
? abbreviateNumber(item?.growth) + "%"
|
|
||||||
: "-"}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr></tbody
|
|
||||||
>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<Lazy>
|
||||||
<h2 class="mb-2 text-xl font-bold">EPS Forecast</h2>
|
<EstimationGraph
|
||||||
<div class="rounded-sm border p-2 border-gray-600">
|
userTier={data?.user?.tier}
|
||||||
<div class="app h-[275px] w-full">
|
title="EPS"
|
||||||
{#if optionsEPS !== null}
|
options={optionsEPS}
|
||||||
<Chart {init} options={optionsEPS} class="chart" />
|
tableDataList={epsDateList}
|
||||||
{/if}
|
highDataList={highEPSList}
|
||||||
</div>
|
avgDataList={avgEPSList}
|
||||||
<div
|
lowDataList={lowEPSList}
|
||||||
class="mt-3 overflow-x-auto p-0 text-center sm:p-0.5 lg:mt-3.5"
|
/>
|
||||||
data-test="forecast-estimate-table"
|
</Lazy>
|
||||||
>
|
|
||||||
<table class="w-full text-right">
|
<Lazy>
|
||||||
<thead
|
<EstimationGraph
|
||||||
><tr class="border-b border-gray-600 align-bottom font-normal"
|
userTier={data?.user?.tier}
|
||||||
><th
|
title="EPS Growth"
|
||||||
class="p-1 text-left font-semibold text-sm sm:text-[1rem]"
|
options={optionsEPSGrowth}
|
||||||
>EPS</th
|
tableDataList={epsDateList}
|
||||||
>
|
highDataList={highEPSList}
|
||||||
{#each epsDateList as date, index}
|
avgDataList={avgEPSList}
|
||||||
<th class="p-1 font-semibold text-sm sm:text-[1rem]">
|
lowDataList={lowEPSList}
|
||||||
{#if index !== 0}{date}{/if}</th
|
/>
|
||||||
>
|
</Lazy>
|
||||||
{/each}
|
|
||||||
</tr></thead
|
|
||||||
>
|
|
||||||
<tbody
|
|
||||||
><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>High</td
|
|
||||||
>
|
|
||||||
{#each highEPSList as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= highEPSList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
{abbreviateNumber(item?.val)}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Avg</td
|
|
||||||
>
|
|
||||||
{#each avgEPSList as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= avgEPSList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
{abbreviateNumber(item?.val)}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Low</td
|
|
||||||
>
|
|
||||||
{#each lowEPSList as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= lowEPSList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
{abbreviateNumber(item?.val)}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr></tbody
|
|
||||||
>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 class="mb-2 text-xl font-bold">EPS Growth</h2>
|
|
||||||
<div class="rounded-sm border p-2 border-gray-600">
|
|
||||||
<div class="app h-[275px] w-full">
|
|
||||||
{#if optionsEPSGrowth !== null}
|
|
||||||
<Chart {init} options={optionsEPSGrowth} class="chart" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="mt-3 overflow-x-auto p-0 text-center sm:p-0.5 lg:mt-3.5"
|
|
||||||
>
|
|
||||||
<table class="w-full text-right">
|
|
||||||
<thead
|
|
||||||
><tr class="border-b border-gray-600 align-bottom font-normal"
|
|
||||||
><th
|
|
||||||
class="p-1 text-left font-semibold text-sm sm:text-[1rem]"
|
|
||||||
>EPS Growth</th
|
|
||||||
>
|
|
||||||
{#each epsDateList as date, index}
|
|
||||||
<th class="p-1 font-semibold text-sm sm:text-[1rem]"
|
|
||||||
>{#if index !== 0}{date}{/if}</th
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</tr></thead
|
|
||||||
>
|
|
||||||
<tbody
|
|
||||||
><tr
|
|
||||||
class="border-b border-gray-600 last:border-0 whitespace-nowrap"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>High</td
|
|
||||||
>
|
|
||||||
{#each computeGrowthSingleList(highEPSList, avgEPSList) as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= highEPSList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
<span
|
|
||||||
class={item?.growth !== null && item?.growth > 0
|
|
||||||
? "text-[#00FC50] before:content-['+']"
|
|
||||||
: item?.growth < 0
|
|
||||||
? "text-[#FF2F1F]"
|
|
||||||
: "text-white"}
|
|
||||||
>
|
|
||||||
{item?.growth !== null &&
|
|
||||||
Math.abs(item?.growth - 0) > 0
|
|
||||||
? abbreviateNumber(item?.growth) + "%"
|
|
||||||
: "-"}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Avg</td
|
|
||||||
>
|
|
||||||
{#each epsAvgGrowthList?.filter((item) => item.FY >= 24) as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= avgEPSList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
<span
|
|
||||||
class={item?.growth !== null && item?.growth > 0
|
|
||||||
? "text-[#00FC50] before:content-['+']"
|
|
||||||
: item?.growth < 0
|
|
||||||
? "text-[#FF2F1F]"
|
|
||||||
: "text-white"}
|
|
||||||
>
|
|
||||||
{item?.growth !== null &&
|
|
||||||
Math.abs(item?.growth - 0) > 0
|
|
||||||
? abbreviateNumber(item?.growth) + "%"
|
|
||||||
: "-"}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr><tr class="border-b border-gray-600 last:border-0"
|
|
||||||
><td class="whitespace-nowrap px-1 py-[3px] text-left"
|
|
||||||
>Low</td
|
|
||||||
>
|
|
||||||
{#each computeGrowthSingleList(lowEPSList, avgEPSList) as item, index}
|
|
||||||
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
|
||||||
{#if index !== 0}
|
|
||||||
{#if data?.user?.tier !== "Pro" && index >= lowEPSList?.length - 2}
|
|
||||||
<a
|
|
||||||
class="inline-block ml-0.5 text-white"
|
|
||||||
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}
|
|
||||||
<span
|
|
||||||
class={item?.growth !== null && item?.growth > 0
|
|
||||||
? "text-[#00FC50] before:content-['+']"
|
|
||||||
: item?.growth < 0
|
|
||||||
? "text-[#FF2F1F]"
|
|
||||||
: "text-white"}
|
|
||||||
>
|
|
||||||
{item?.growth !== null &&
|
|
||||||
Math.abs(item?.growth - 0) > 0
|
|
||||||
? abbreviateNumber(item?.growth) + "%"
|
|
||||||
: "-"}
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
{/each}
|
|
||||||
</tr></tbody
|
|
||||||
>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
140
src/lib/components/EstimationGraph.svelte
Normal file
140
src/lib/components/EstimationGraph.svelte
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Chart } from "svelte-echarts";
|
||||||
|
import { init, use } from "echarts/core";
|
||||||
|
import { LineChart, CustomChart } from "echarts/charts";
|
||||||
|
import { GridComponent, TooltipComponent } from "echarts/components";
|
||||||
|
import { CanvasRenderer } from "echarts/renderers";
|
||||||
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
|
use([
|
||||||
|
LineChart,
|
||||||
|
CustomChart,
|
||||||
|
GridComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
CanvasRenderer,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export let userTier;
|
||||||
|
export let title;
|
||||||
|
export let options;
|
||||||
|
export let tableDataList;
|
||||||
|
export let highDataList;
|
||||||
|
export let avgDataList;
|
||||||
|
export let lowDataList;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="mb-2 text-xl font-bold">{title} Forecast</h2>
|
||||||
|
<div class="rounded-sm border p-2 border-gray-600">
|
||||||
|
<div class="app h-[275px] w-full">
|
||||||
|
{#if options !== null}
|
||||||
|
<Chart {init} {options} class="chart" />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 overflow-x-auto p-0 text-center sm:p-0.5 lg:mt-3.5">
|
||||||
|
<table class="w-full text-right">
|
||||||
|
<thead
|
||||||
|
><tr
|
||||||
|
class="border-b border-gray-600 align-bottom text-white font-normal"
|
||||||
|
><th class="p-1 text-left font-semibold text-sm sm:text-[1rem]"
|
||||||
|
>{title}</th
|
||||||
|
>
|
||||||
|
{#each tableDataList as date, index}
|
||||||
|
<th class="p-1 font-semibold text-sm sm:text-[1rem]">
|
||||||
|
{#if index !== 0}{date}{/if}</th
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</tr></thead
|
||||||
|
>
|
||||||
|
<tbody
|
||||||
|
><tr class="border-b border-gray-600 last:border-0"
|
||||||
|
><td class="whitespace-nowrap px-1 py-[3px] text-left">High</td>
|
||||||
|
{#each highDataList as item, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#if index !== 0}
|
||||||
|
{#if userTier !== "Pro" && index >= highDataList?.length - 2}
|
||||||
|
<a class="inline-block ml-0.5 text-white" 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}
|
||||||
|
{abbreviateNumber(item?.val)}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr><tr class="border-b border-gray-600 last:border-0"
|
||||||
|
><td class="whitespace-nowrap px-1 py-[3px] text-left">Avg</td>
|
||||||
|
{#each avgDataList as item, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#if index !== 0}
|
||||||
|
{#if userTier !== "Pro" && index >= avgDataList?.length - 2}
|
||||||
|
<a class="inline-block ml-0.5 text-white" 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}
|
||||||
|
{abbreviateNumber(item?.val)}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr><tr class="border-b border-gray-600 last:border-0"
|
||||||
|
><td class="whitespace-nowrap px-1 py-[3px] text-left">Low</td>
|
||||||
|
{#each lowDataList as item, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#if index !== 0}
|
||||||
|
{#if userTier !== "Pro" && index >= lowDataList?.length - 2}
|
||||||
|
<a class="inline-block ml-0.5 text-white" 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}
|
||||||
|
{abbreviateNumber(item?.val)}
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr></tbody
|
||||||
|
>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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>
|
||||||
@ -135,30 +135,29 @@
|
|||||||
|
|
||||||
function sendMessage(message) {
|
function sendMessage(message) {
|
||||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||||
socket.send(message);
|
socket.send(JSON?.stringify(message));
|
||||||
} else {
|
} else {
|
||||||
console.error("WebSocket is not open. Unable to send message.");
|
console.error("WebSocket is not open. Unable to send message.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function websocketRealtimeData() {
|
async function websocketRealtimeData() {
|
||||||
previousTicker = $etfTicker;
|
|
||||||
try {
|
try {
|
||||||
socket = new WebSocket(data?.wsURL + "/realtime-data");
|
socket = new WebSocket(data?.wsURL + "/price-data");
|
||||||
|
|
||||||
socket.addEventListener("open", () => {
|
socket.addEventListener("open", () => {
|
||||||
//console.log('WebSocket connection opened');
|
console.log("WebSocket connection opened");
|
||||||
|
// Send only current watchlist symbols
|
||||||
// Send the initial value of etfTicker
|
const tickerList = [$etfTicker] || [];
|
||||||
sendMessage($etfTicker?.toLowerCase());
|
sendMessage(tickerList);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
//console.log('Received message:', data);
|
console.log("Received message:", data);
|
||||||
try {
|
try {
|
||||||
const parsedData = JSON.parse(data);
|
const parsedData = JSON.parse(data);
|
||||||
const { type, lp, time, bp, ap } = parsedData || {};
|
const { type, lp, time, bp, ap, avgPrice } = parsedData?.at(0) || {};
|
||||||
|
|
||||||
if (type === "T") {
|
if (type === "T") {
|
||||||
$realtimePrice = typeof lp !== "undefined" ? lp : null;
|
$realtimePrice = typeof lp !== "undefined" ? lp : null;
|
||||||
@ -170,7 +169,8 @@
|
|||||||
} else if (type === "Q") {
|
} else if (type === "Q") {
|
||||||
$wsBidPrice = typeof bp !== "undefined" ? bp : null;
|
$wsBidPrice = typeof bp !== "undefined" ? bp : null;
|
||||||
$wsAskPrice = typeof ap !== "undefined" ? ap : null;
|
$wsAskPrice = typeof ap !== "undefined" ? ap : null;
|
||||||
$realtimePrice = $wsAskPrice;
|
$realtimePrice =
|
||||||
|
typeof avgPrice !== "undefined" ? avgPrice?.toFixed(2) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update price increase state
|
// Update price increase state
|
||||||
@ -187,11 +187,9 @@
|
|||||||
|
|
||||||
socket.addEventListener("close", (event) => {
|
socket.addEventListener("close", (event) => {
|
||||||
console.log("WebSocket connection closed:", event.reason);
|
console.log("WebSocket connection closed:", event.reason);
|
||||||
// Handle disconnection, you might want to attempt to reconnect here
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("WebSocket connection error:", error);
|
console.error("WebSocket connection error:", error);
|
||||||
// Handle connection errors here
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -144,30 +144,29 @@
|
|||||||
|
|
||||||
function sendMessage(message) {
|
function sendMessage(message) {
|
||||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||||
socket.send(message);
|
socket.send(JSON?.stringify(message));
|
||||||
} else {
|
} else {
|
||||||
console.error("WebSocket is not open. Unable to send message.");
|
console.error("WebSocket is not open. Unable to send message.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function websocketRealtimeData() {
|
async function websocketRealtimeData() {
|
||||||
previousTicker = $stockTicker;
|
|
||||||
try {
|
try {
|
||||||
socket = new WebSocket(data?.wsURL + "/realtime-data");
|
socket = new WebSocket(data?.wsURL + "/price-data");
|
||||||
|
|
||||||
socket.addEventListener("open", () => {
|
socket.addEventListener("open", () => {
|
||||||
//console.log('WebSocket connection opened');
|
console.log("WebSocket connection opened");
|
||||||
|
// Send only current watchlist symbols
|
||||||
// Send the initial value of stockTicker
|
const tickerList = [$stockTicker] || [];
|
||||||
sendMessage($stockTicker?.toLowerCase());
|
sendMessage(tickerList);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener("message", (event) => {
|
socket.addEventListener("message", (event) => {
|
||||||
const data = event.data;
|
const data = event.data;
|
||||||
//console.log('Received message:', data);
|
console.log("Received message:", data);
|
||||||
try {
|
try {
|
||||||
const parsedData = JSON.parse(data);
|
const parsedData = JSON.parse(data);
|
||||||
const { type, lp, time, bp, ap, avgPrice } = parsedData || {};
|
const { type, lp, time, bp, ap, avgPrice } = parsedData?.at(0) || {};
|
||||||
|
|
||||||
if (type === "T") {
|
if (type === "T") {
|
||||||
$realtimePrice = typeof lp !== "undefined" ? lp : null;
|
$realtimePrice = typeof lp !== "undefined" ? lp : null;
|
||||||
@ -179,7 +178,8 @@
|
|||||||
} else if (type === "Q") {
|
} else if (type === "Q") {
|
||||||
$wsBidPrice = typeof bp !== "undefined" ? bp : null;
|
$wsBidPrice = typeof bp !== "undefined" ? bp : null;
|
||||||
$wsAskPrice = typeof ap !== "undefined" ? ap : null;
|
$wsAskPrice = typeof ap !== "undefined" ? ap : null;
|
||||||
$realtimePrice = typeof avgPrice !== "undefined" ? avgPrice : null;
|
$realtimePrice =
|
||||||
|
typeof avgPrice !== "undefined" ? avgPrice?.toFixed(2) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update price increase state
|
// Update price increase state
|
||||||
@ -196,11 +196,9 @@
|
|||||||
|
|
||||||
socket.addEventListener("close", (event) => {
|
socket.addEventListener("close", (event) => {
|
||||||
console.log("WebSocket connection closed:", event.reason);
|
console.log("WebSocket connection closed:", event.reason);
|
||||||
// Handle disconnection, you might want to attempt to reconnect here
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("WebSocket connection error:", error);
|
console.error("WebSocket connection error:", error);
|
||||||
// Handle connection errors here
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -183,7 +183,7 @@
|
|||||||
|
|
||||||
async function websocketRealtimeData() {
|
async function websocketRealtimeData() {
|
||||||
try {
|
try {
|
||||||
socket = new WebSocket(data?.wsURL + "/multiple-realtime-data");
|
socket = new WebSocket(data?.wsURL + "/price-data");
|
||||||
|
|
||||||
socket.addEventListener("open", () => {
|
socket.addEventListener("open", () => {
|
||||||
console.log("WebSocket connection opened");
|
console.log("WebSocket connection opened");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user