bugfixing websocket
This commit is contained in:
parent
36b7a25e90
commit
681d9cc423
@ -1,21 +1,10 @@
|
||||
<script lang="ts">
|
||||
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 EstimationGraph from "$lib/components/EstimationGraph.svelte";
|
||||
import Lazy from "svelte-lazy";
|
||||
|
||||
export let data;
|
||||
use([
|
||||
LineChart,
|
||||
CustomChart,
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
CanvasRenderer,
|
||||
]);
|
||||
|
||||
let analystEstimateList = [];
|
||||
let isLoaded = false;
|
||||
@ -829,530 +818,53 @@
|
||||
{/if}
|
||||
|
||||
<div class="space-y-6 lg:grid lg:grid-cols-2 lg:gap-6 lg:space-y-0 mt-10">
|
||||
<div>
|
||||
<h2 class="mb-2 text-xl font-bold">Revenue Forecast</h2>
|
||||
<div class="rounded-sm border p-2 border-gray-600">
|
||||
<div class="app h-[275px] w-full">
|
||||
{#if optionsRevenue !== null}
|
||||
<Chart {init} options={optionsRevenue} 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]"
|
||||
>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>
|
||||
<Lazy>
|
||||
<EstimationGraph
|
||||
userTier={data?.user?.tier}
|
||||
title="Revenue"
|
||||
options={optionsRevenue}
|
||||
tableDataList={revenueDateList}
|
||||
highDataList={highRevenueList}
|
||||
avgDataList={avgRevenueList}
|
||||
lowDataList={lowRevenueList}
|
||||
/>
|
||||
</Lazy>
|
||||
|
||||
<div>
|
||||
<h2 class="mb-2 text-xl font-bold">Revenue Growth</h2>
|
||||
<div class="rounded-sm border p-2 border-gray-600">
|
||||
<div class="app h-[275px] w-full">
|
||||
{#if optionsRevenueGrowth !== null}
|
||||
<Chart {init} options={optionsRevenueGrowth} 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 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>
|
||||
<Lazy>
|
||||
<EstimationGraph
|
||||
userTier={data?.user?.tier}
|
||||
title="Revenue Growth"
|
||||
options={optionsRevenueGrowth}
|
||||
tableDataList={revenueDateList}
|
||||
highDataList={highRevenueList}
|
||||
avgDataList={avgRevenueList}
|
||||
lowDataList={lowRevenueList}
|
||||
/>
|
||||
</Lazy>
|
||||
|
||||
<div>
|
||||
<h2 class="mb-2 text-xl font-bold">EPS Forecast</h2>
|
||||
<div class="rounded-sm border p-2 border-gray-600">
|
||||
<div class="app h-[275px] w-full">
|
||||
{#if optionsEPS !== null}
|
||||
<Chart {init} options={optionsEPS} class="chart" />
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
class="mt-3 overflow-x-auto p-0 text-center sm:p-0.5 lg:mt-3.5"
|
||||
data-test="forecast-estimate-table"
|
||||
>
|
||||
<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</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"
|
||||
><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>
|
||||
<Lazy>
|
||||
<EstimationGraph
|
||||
userTier={data?.user?.tier}
|
||||
title="EPS"
|
||||
options={optionsEPS}
|
||||
tableDataList={epsDateList}
|
||||
highDataList={highEPSList}
|
||||
avgDataList={avgEPSList}
|
||||
lowDataList={lowEPSList}
|
||||
/>
|
||||
</Lazy>
|
||||
|
||||
<Lazy>
|
||||
<EstimationGraph
|
||||
userTier={data?.user?.tier}
|
||||
title="EPS Growth"
|
||||
options={optionsEPSGrowth}
|
||||
tableDataList={epsDateList}
|
||||
highDataList={highEPSList}
|
||||
avgDataList={avgEPSList}
|
||||
lowDataList={lowEPSList}
|
||||
/>
|
||||
</Lazy>
|
||||
</div>
|
||||
</div>
|
||||
</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) {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(message);
|
||||
socket.send(JSON?.stringify(message));
|
||||
} else {
|
||||
console.error("WebSocket is not open. Unable to send message.");
|
||||
}
|
||||
}
|
||||
|
||||
async function websocketRealtimeData() {
|
||||
previousTicker = $etfTicker;
|
||||
try {
|
||||
socket = new WebSocket(data?.wsURL + "/realtime-data");
|
||||
socket = new WebSocket(data?.wsURL + "/price-data");
|
||||
|
||||
socket.addEventListener("open", () => {
|
||||
//console.log('WebSocket connection opened');
|
||||
|
||||
// Send the initial value of etfTicker
|
||||
sendMessage($etfTicker?.toLowerCase());
|
||||
console.log("WebSocket connection opened");
|
||||
// Send only current watchlist symbols
|
||||
const tickerList = [$etfTicker] || [];
|
||||
sendMessage(tickerList);
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const data = event.data;
|
||||
//console.log('Received message:', data);
|
||||
console.log("Received message:", data);
|
||||
try {
|
||||
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") {
|
||||
$realtimePrice = typeof lp !== "undefined" ? lp : null;
|
||||
@ -170,7 +169,8 @@
|
||||
} else if (type === "Q") {
|
||||
$wsBidPrice = typeof bp !== "undefined" ? bp : null;
|
||||
$wsAskPrice = typeof ap !== "undefined" ? ap : null;
|
||||
$realtimePrice = $wsAskPrice;
|
||||
$realtimePrice =
|
||||
typeof avgPrice !== "undefined" ? avgPrice?.toFixed(2) : null;
|
||||
}
|
||||
|
||||
// Update price increase state
|
||||
@ -187,11 +187,9 @@
|
||||
|
||||
socket.addEventListener("close", (event) => {
|
||||
console.log("WebSocket connection closed:", event.reason);
|
||||
// Handle disconnection, you might want to attempt to reconnect here
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("WebSocket connection error:", error);
|
||||
// Handle connection errors here
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -144,30 +144,29 @@
|
||||
|
||||
function sendMessage(message) {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(message);
|
||||
socket.send(JSON?.stringify(message));
|
||||
} else {
|
||||
console.error("WebSocket is not open. Unable to send message.");
|
||||
}
|
||||
}
|
||||
|
||||
async function websocketRealtimeData() {
|
||||
previousTicker = $stockTicker;
|
||||
try {
|
||||
socket = new WebSocket(data?.wsURL + "/realtime-data");
|
||||
socket = new WebSocket(data?.wsURL + "/price-data");
|
||||
|
||||
socket.addEventListener("open", () => {
|
||||
//console.log('WebSocket connection opened');
|
||||
|
||||
// Send the initial value of stockTicker
|
||||
sendMessage($stockTicker?.toLowerCase());
|
||||
console.log("WebSocket connection opened");
|
||||
// Send only current watchlist symbols
|
||||
const tickerList = [$stockTicker] || [];
|
||||
sendMessage(tickerList);
|
||||
});
|
||||
|
||||
socket.addEventListener("message", (event) => {
|
||||
const data = event.data;
|
||||
//console.log('Received message:', data);
|
||||
console.log("Received message:", data);
|
||||
try {
|
||||
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") {
|
||||
$realtimePrice = typeof lp !== "undefined" ? lp : null;
|
||||
@ -179,7 +178,8 @@
|
||||
} else if (type === "Q") {
|
||||
$wsBidPrice = typeof bp !== "undefined" ? bp : 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
|
||||
@ -196,11 +196,9 @@
|
||||
|
||||
socket.addEventListener("close", (event) => {
|
||||
console.log("WebSocket connection closed:", event.reason);
|
||||
// Handle disconnection, you might want to attempt to reconnect here
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("WebSocket connection error:", error);
|
||||
// Handle connection errors here
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -183,7 +183,7 @@
|
||||
|
||||
async function websocketRealtimeData() {
|
||||
try {
|
||||
socket = new WebSocket(data?.wsURL + "/multiple-realtime-data");
|
||||
socket = new WebSocket(data?.wsURL + "/price-data");
|
||||
|
||||
socket.addEventListener("open", () => {
|
||||
console.log("WebSocket connection opened");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user