update analyst page
This commit is contained in:
parent
d1344a1c23
commit
e37ab29ce0
@ -17,6 +17,8 @@
|
|||||||
let xData = [];
|
let xData = [];
|
||||||
let optionsRevenue = null;
|
let optionsRevenue = null;
|
||||||
let optionsEPS = null;
|
let optionsEPS = null;
|
||||||
|
let optionsRevenueGrowth = null;
|
||||||
|
let optionsEPSGrowth = null;
|
||||||
let revenueDateList = [];
|
let revenueDateList = [];
|
||||||
let avgRevenueList = [];
|
let avgRevenueList = [];
|
||||||
let lowRevenueList = [];
|
let lowRevenueList = [];
|
||||||
@ -26,8 +28,53 @@
|
|||||||
let lowEPSList = [];
|
let lowEPSList = [];
|
||||||
let highEPSList = [];
|
let highEPSList = [];
|
||||||
|
|
||||||
|
let revenueGrowthList = [];
|
||||||
|
let epsGrowthList = [];
|
||||||
|
|
||||||
let displayData = "Revenue";
|
let displayData = "Revenue";
|
||||||
|
|
||||||
|
function computeGrowthList(tableActualRevenue, tableForecastRevenue) {
|
||||||
|
return tableActualRevenue?.map((item, index) => {
|
||||||
|
// If it's the first item or the list is empty, return null
|
||||||
|
if (index === 0 || tableActualRevenue.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If actual value is null, check forecast
|
||||||
|
if (item?.val === null) {
|
||||||
|
const prevForecastVal = tableForecastRevenue[index - 1]?.val ?? 0;
|
||||||
|
const currentForecastVal = tableForecastRevenue[index]?.val ?? 0;
|
||||||
|
|
||||||
|
// Avoid division by zero when calculating forecast growth
|
||||||
|
if (prevForecastVal === 0 || currentForecastVal === 0) {
|
||||||
|
return null; // Return null if previous or current forecast is 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const forecastGrowth =
|
||||||
|
((currentForecastVal - prevForecastVal) / Math.abs(prevForecastVal)) *
|
||||||
|
100;
|
||||||
|
|
||||||
|
// Return rounded forecast growth value, or null if 0
|
||||||
|
return forecastGrowth !== 0 ? Number(forecastGrowth.toFixed(2)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute actual growth for non-null actual values
|
||||||
|
const prevActualVal = tableActualRevenue[index - 1]?.val ?? 0;
|
||||||
|
const currentActualVal = item?.val ?? 0;
|
||||||
|
|
||||||
|
// Avoid division by zero when calculating actual growth
|
||||||
|
if (prevActualVal === 0 || currentActualVal === 0) {
|
||||||
|
return null; // Return null if previous or current actual value is 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualGrowth =
|
||||||
|
((currentActualVal - prevActualVal) / Math.abs(prevActualVal)) * 100;
|
||||||
|
|
||||||
|
// Return rounded actual growth value, or null if 0
|
||||||
|
return actualGrowth !== 0 ? Number(actualGrowth.toFixed(2)) : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function findIndex(data) {
|
function findIndex(data) {
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
@ -105,8 +152,8 @@
|
|||||||
},
|
},
|
||||||
animation: false,
|
animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
left: "2%",
|
left: "5%",
|
||||||
right: "2%",
|
right: "5%",
|
||||||
bottom: "2%",
|
bottom: "2%",
|
||||||
top: "5%",
|
top: "5%",
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
@ -183,6 +230,56 @@
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const optionsGrowth = {
|
||||||
|
animation: false,
|
||||||
|
grid: {
|
||||||
|
left: "5%",
|
||||||
|
right: "5%",
|
||||||
|
bottom: "2%",
|
||||||
|
top: "5%",
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
boundaryGap: false,
|
||||||
|
data: dates,
|
||||||
|
axisLabel: {
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: "value",
|
||||||
|
splitLine: {
|
||||||
|
show: false, // Disable x-axis grid lines
|
||||||
|
},
|
||||||
|
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // Hide y-axis labels
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: dataType === "Revenue" ? "Revenue Growth" : "EPS Growth",
|
||||||
|
data: (dataType === "Revenue" ? revenueGrowthList : epsGrowthList)
|
||||||
|
?.slice(1)
|
||||||
|
.map((value) => ({
|
||||||
|
value,
|
||||||
|
itemStyle: {
|
||||||
|
color: value >= 0 ? "#00FC50" : "#D9220E", // Green for >= 0, Red for < 0
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
type: "bar",
|
||||||
|
smooth: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let currentYearSuffix = new Date().getFullYear().toString().slice(-2);
|
let currentYearSuffix = new Date().getFullYear().toString().slice(-2);
|
||||||
let searchString = `FY${currentYearSuffix}`;
|
let searchString = `FY${currentYearSuffix}`;
|
||||||
|
|
||||||
@ -190,6 +287,7 @@
|
|||||||
if (dataType === "Revenue") {
|
if (dataType === "Revenue") {
|
||||||
let currentYearIndex = dates?.findIndex((date) => date === searchString);
|
let currentYearIndex = dates?.findIndex((date) => date === searchString);
|
||||||
optionsRevenue = option;
|
optionsRevenue = option;
|
||||||
|
optionsRevenueGrowth = optionsGrowth;
|
||||||
revenueDateList = dates?.slice(currentYearIndex) || [];
|
revenueDateList = dates?.slice(currentYearIndex) || [];
|
||||||
avgRevenueList = avgList?.slice(currentYearIndex) || [];
|
avgRevenueList = avgList?.slice(currentYearIndex) || [];
|
||||||
lowRevenueList = lowList?.slice(currentYearIndex) || [];
|
lowRevenueList = lowList?.slice(currentYearIndex) || [];
|
||||||
@ -198,6 +296,8 @@
|
|||||||
let currentYearIndex = dates?.findIndex((date) => date === searchString);
|
let currentYearIndex = dates?.findIndex((date) => date === searchString);
|
||||||
|
|
||||||
optionsEPS = option;
|
optionsEPS = option;
|
||||||
|
optionsEPSGrowth = optionsGrowth;
|
||||||
|
|
||||||
epsDateList = dates?.slice(currentYearIndex) || [];
|
epsDateList = dates?.slice(currentYearIndex) || [];
|
||||||
avgEPSList = avgList?.slice(currentYearIndex) || [];
|
avgEPSList = avgList?.slice(currentYearIndex) || [];
|
||||||
lowEPSList = lowList?.slice(currentYearIndex) || [];
|
lowEPSList = lowList?.slice(currentYearIndex) || [];
|
||||||
@ -213,6 +313,9 @@
|
|||||||
tableActualEPS = [];
|
tableActualEPS = [];
|
||||||
tableForecastEPS = [];
|
tableForecastEPS = [];
|
||||||
|
|
||||||
|
revenueGrowthList = [];
|
||||||
|
epsGrowthList = [];
|
||||||
|
|
||||||
let filteredData =
|
let filteredData =
|
||||||
analystEstimateList?.filter((item) => item.date >= 2015) ?? [];
|
analystEstimateList?.filter((item) => item.date >= 2015) ?? [];
|
||||||
|
|
||||||
@ -229,38 +332,26 @@
|
|||||||
numOfAnalysts: item?.numOfAnalysts,
|
numOfAnalysts: item?.numOfAnalysts,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//EPS Data
|
//EPS Data
|
||||||
let forwardPeStart = false;
|
|
||||||
filteredData?.forEach((item) => {
|
filteredData?.forEach((item) => {
|
||||||
const fy = Number(String(item?.date)?.slice(-2));
|
|
||||||
const actualVal = item?.eps ?? null;
|
|
||||||
const forecastVal = item?.estimatedEpsAvg;
|
|
||||||
|
|
||||||
tableActualEPS?.push({
|
tableActualEPS?.push({
|
||||||
FY: fy,
|
FY: Number(String(item?.date)?.slice(-2)),
|
||||||
val: actualVal,
|
val: item?.eps,
|
||||||
|
});
|
||||||
|
tableForecastEPS?.push({
|
||||||
|
FY: Number(String(item?.date)?.slice(-2)),
|
||||||
|
val: item?.estimatedEpsAvg,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (actualVal === null) {
|
|
||||||
forwardPeStart = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const forecastEntry: any = {
|
|
||||||
FY: fy,
|
|
||||||
val: forecastVal,
|
|
||||||
numOfAnalysts: item?.numOfAnalysts,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add forwardPe if the condition is met
|
|
||||||
if (forwardPeStart && forecastVal !== null) {
|
|
||||||
forecastEntry.forwardPe =
|
|
||||||
Math.round((data.getStockQuote.price / forecastVal) * 100) / 100;
|
|
||||||
} else {
|
|
||||||
forecastEntry.forwardPe = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
tableForecastEPS?.push(forecastEntry);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Values coincide with table values for crosscheck
|
||||||
|
revenueGrowthList = computeGrowthList(
|
||||||
|
tableActualRevenue,
|
||||||
|
tableForecastRevenue,
|
||||||
|
);
|
||||||
|
|
||||||
|
epsGrowthList = computeGrowthList(tableActualEPS, tableForecastEPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@ -269,11 +360,10 @@
|
|||||||
analystEstimateList = [];
|
analystEstimateList = [];
|
||||||
analystEstimateList = data?.getAnalystEstimate || [];
|
analystEstimateList = data?.getAnalystEstimate || [];
|
||||||
if (analystEstimateList?.length !== 0) {
|
if (analystEstimateList?.length !== 0) {
|
||||||
|
prepareData();
|
||||||
$analystEstimateComponent = true;
|
$analystEstimateComponent = true;
|
||||||
getPlotOptions("Revenue");
|
getPlotOptions("Revenue");
|
||||||
getPlotOptions("EPS");
|
getPlotOptions("EPS");
|
||||||
|
|
||||||
prepareData();
|
|
||||||
} else {
|
} else {
|
||||||
$analystEstimateComponent = false;
|
$analystEstimateComponent = false;
|
||||||
}
|
}
|
||||||
@ -335,66 +425,30 @@
|
|||||||
>
|
>
|
||||||
Revenue Growth
|
Revenue Growth
|
||||||
</th>
|
</th>
|
||||||
{#each tableActualRevenue as item, index}
|
{#each computeGrowthList(tableActualRevenue, tableForecastRevenue) as growth, index}
|
||||||
<td
|
<td
|
||||||
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
||||||
>
|
>
|
||||||
{#if index === 0 || tableActualRevenue?.length === 0}
|
{#if index === 0 || growth === null}
|
||||||
n/a
|
n/a
|
||||||
{:else if item?.val === null}
|
{:else if tableActualRevenue[index]?.val === null}
|
||||||
{#if tableForecastRevenue[index]?.val - tableForecastRevenue[index - 1]?.val > 0}
|
<span
|
||||||
<span class="text-orange-400 before:content-['+']">
|
class="text-orange-400 {growth > 0
|
||||||
{(() => {
|
? "before:content-['+']"
|
||||||
const previousVal =
|
: ''}"
|
||||||
tableForecastRevenue[index - 1]?.val ?? 0;
|
>
|
||||||
const currentVal =
|
{growth}%*
|
||||||
tableForecastRevenue[index]?.val ?? 0;
|
|
||||||
const change =
|
|
||||||
((currentVal - previousVal) /
|
|
||||||
Math.abs(previousVal)) *
|
|
||||||
100;
|
|
||||||
return isFinite(change)
|
|
||||||
? `${change.toFixed(2)}%*`
|
|
||||||
: "n/a";
|
|
||||||
})()}
|
|
||||||
</span>
|
|
||||||
{:else if tableForecastRevenue[index]?.val - tableForecastRevenue[index - 1]?.val < 0}
|
|
||||||
<span class="text-orange-400">
|
|
||||||
{(() => {
|
|
||||||
const previousVal =
|
|
||||||
tableForecastRevenue[index - 1]?.val ?? 0;
|
|
||||||
const currentVal =
|
|
||||||
tableForecastRevenue[index]?.val ?? 0;
|
|
||||||
const change =
|
|
||||||
((currentVal - previousVal) /
|
|
||||||
Math.abs(previousVal)) *
|
|
||||||
100;
|
|
||||||
return isFinite(change)
|
|
||||||
? `${change.toFixed(2)}%*`
|
|
||||||
: "n/a";
|
|
||||||
})()}
|
|
||||||
</span>
|
|
||||||
{:else}
|
|
||||||
n/a
|
|
||||||
{/if}
|
|
||||||
{:else if item?.val - tableActualRevenue[index - 1]?.val > 0}
|
|
||||||
<span class="text-[#00FC50] before:content-['+']">
|
|
||||||
{(
|
|
||||||
((item?.val - tableActualRevenue[index - 1]?.val) /
|
|
||||||
Math.abs(tableActualRevenue[index - 1]?.val)) *
|
|
||||||
100
|
|
||||||
)?.toFixed(2)}%
|
|
||||||
</span>
|
|
||||||
{:else if item?.val - tableActualRevenue[index - 1]?.val < 0}
|
|
||||||
<span class="text-[#FF2F1F]">
|
|
||||||
{(
|
|
||||||
((item?.val - tableActualRevenue[index - 1]?.val) /
|
|
||||||
Math.abs(tableActualRevenue[index - 1]?.val)) *
|
|
||||||
100
|
|
||||||
)?.toFixed(2)}%
|
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
0.00%
|
<span
|
||||||
|
class={growth > 0
|
||||||
|
? "text-[#00FC50] before:content-['+']"
|
||||||
|
: growth < 0
|
||||||
|
? "text-[#FF2F1F]"
|
||||||
|
: ""}
|
||||||
|
>
|
||||||
|
{growth}%
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
@ -425,50 +479,30 @@
|
|||||||
>
|
>
|
||||||
EPS Growth
|
EPS Growth
|
||||||
</th>
|
</th>
|
||||||
{#each tableActualEPS as item, index}
|
{#each computeGrowthList(tableActualEPS, tableForecastEPS) as growth, index}
|
||||||
<td
|
<td
|
||||||
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
class="text-white text-sm sm:text-[1rem] text-end font-medium bg-[#09090B]"
|
||||||
>
|
>
|
||||||
{#if index === 0 || tableActualEPS?.length === 0}
|
{#if index === 0 || growth === null}
|
||||||
n/a
|
n/a
|
||||||
{:else if item?.val === null}
|
{:else if tableActualRevenue[index]?.val === null}
|
||||||
{#if tableForecastEPS[index]?.val - tableForecastEPS[index - 1]?.val > 0}
|
<span
|
||||||
<span class="text-orange-400 before:content-['+']">
|
class="text-orange-400 {growth > 0
|
||||||
{(
|
? "before:content-['+']"
|
||||||
((tableForecastEPS[index]?.val -
|
: ''}"
|
||||||
tableForecastEPS[index - 1]?.val) /
|
>
|
||||||
Math.abs(tableForecastEPS[index - 1]?.val)) *
|
{growth}%*
|
||||||
100
|
|
||||||
)?.toFixed(2)}%*
|
|
||||||
</span>
|
|
||||||
{:else if tableForecastEPS[index]?.val - tableForecastEPS[index - 1]?.val < 0}
|
|
||||||
<span class="text-orange-400">
|
|
||||||
{(
|
|
||||||
((tableForecastEPS[index]?.val -
|
|
||||||
tableForecastEPS[index - 1]?.val) /
|
|
||||||
Math.abs(tableForecastEPS[index - 1]?.val)) *
|
|
||||||
100
|
|
||||||
)?.toFixed(2)}%*
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
{:else if item?.val - tableActualEPS[index - 1]?.val > 0}
|
|
||||||
<span class="text-[#00FC50] before:content-['+']">
|
|
||||||
{(
|
|
||||||
((item?.val - tableActualEPS[index - 1]?.val) /
|
|
||||||
Math.abs(tableActualEPS[index - 1]?.val)) *
|
|
||||||
100
|
|
||||||
)?.toFixed(2)}%
|
|
||||||
</span>
|
|
||||||
{:else if item?.val - tableActualEPS[index - 1]?.val < 0}
|
|
||||||
<span class="text-[#FF2F1F]">
|
|
||||||
{(
|
|
||||||
((item?.val - tableActualEPS[index - 1]?.val) /
|
|
||||||
Math.abs(tableActualEPS[index - 1]?.val)) *
|
|
||||||
100
|
|
||||||
)?.toFixed(2)}%
|
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
0.00%
|
<span
|
||||||
|
class={growth > 0
|
||||||
|
? "text-[#00FC50] before:content-['+']"
|
||||||
|
: growth < 0
|
||||||
|
? "text-[#FF2F1F]"
|
||||||
|
: ""}
|
||||||
|
>
|
||||||
|
{growth}%
|
||||||
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
@ -647,6 +681,115 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<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"
|
||||||
|
><th
|
||||||
|
class="p-1 text-left font-semibold text-sm sm:text-[1rem]"
|
||||||
|
>Revenue Growth</th
|
||||||
|
>
|
||||||
|
{#each revenueDateList as date}
|
||||||
|
<th class="p-1 font-semibold text-sm sm:text-[1rem]"
|
||||||
|
>{date}</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 val, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#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(val)}
|
||||||
|
{/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 val, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#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(val)}
|
||||||
|
{/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 val, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#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(val)}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr></tbody
|
||||||
|
>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 class="mb-2 text-xl font-bold">EPS Forecast</h2>
|
<h2 class="mb-2 text-xl font-bold">EPS Forecast</h2>
|
||||||
<div class="rounded-sm border p-2 border-gray-600">
|
<div class="rounded-sm border p-2 border-gray-600">
|
||||||
@ -755,6 +898,114 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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"
|
||||||
|
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 Growth</th
|
||||||
|
>
|
||||||
|
{#each epsDateList as date}
|
||||||
|
<th class="p-1 font-semibold text-sm sm:text-[1rem]"
|
||||||
|
>{date}</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 val, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#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(val)}
|
||||||
|
{/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 val, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#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(val)}
|
||||||
|
{/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 val, index}
|
||||||
|
<td class="px-1 py-[3px] text-sm sm:text-[1rem]">
|
||||||
|
{#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(val)}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
</tr></tbody
|
||||||
|
>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user