ui fixes
This commit is contained in:
parent
811627f30d
commit
edf98dda63
@ -1,11 +1,9 @@
|
|||||||
export const load = async ({ locals, params }) => {
|
export const load = async ({ locals, params }) => {
|
||||||
|
const { apiURL, apiKey } = locals;
|
||||||
|
const postData = {
|
||||||
|
ticker: params.tickerID,
|
||||||
|
};
|
||||||
const getAnalystEstimate = async () => {
|
const getAnalystEstimate = async () => {
|
||||||
const { apiURL, apiKey } = locals;
|
|
||||||
|
|
||||||
const postData = {
|
|
||||||
ticker: params.tickerID,
|
|
||||||
};
|
|
||||||
|
|
||||||
// make the POST request to the endpoint
|
// make the POST request to the endpoint
|
||||||
const response = await fetch(apiURL + "/analyst-estimate", {
|
const response = await fetch(apiURL + "/analyst-estimate", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -21,8 +19,23 @@ export const load = async ({ locals, params }) => {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAnalystInsight = async () => {
|
||||||
|
const response = await fetch(apiURL + "/analyst-insight", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-API-KEY": apiKey,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(postData),
|
||||||
|
});
|
||||||
|
|
||||||
|
const output = await response.json();
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
// Make sure to return a promise
|
// Make sure to return a promise
|
||||||
return {
|
return {
|
||||||
getAnalystEstimate: await getAnalystEstimate(),
|
getAnalystEstimate: await getAnalystEstimate(),
|
||||||
|
getAnalystInsight: await getAnalystInsight(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,17 @@
|
|||||||
} from "$lib/store";
|
} from "$lib/store";
|
||||||
import { abbreviateNumber } from "$lib/utils";
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
|
|
||||||
|
import { Chart } from "svelte-echarts";
|
||||||
|
import { init, use } from "echarts/core";
|
||||||
|
import { BarChart } from "echarts/charts";
|
||||||
|
import { GridComponent, TooltipComponent } from "echarts/components";
|
||||||
|
import { CanvasRenderer } from "echarts/renderers";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
|
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
||||||
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let changeRevenue = 0;
|
let changeRevenue = 0;
|
||||||
let changeNetIncome = 0;
|
let changeNetIncome = 0;
|
||||||
@ -74,6 +84,95 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getPlotOptions() {
|
||||||
|
if (!rawAnalystList || rawAnalystList.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define categories in the exact order you specified
|
||||||
|
const categories = ["Strong Buy", "Buy", "Hold", "Sell", "Strong Sell"];
|
||||||
|
const colors = ["#008A00", "#31B800", "#FF9E21", "#D9220E", "#9E190A"];
|
||||||
|
|
||||||
|
// Create a consistent mapping for data
|
||||||
|
const formattedData = rawAnalystList.map((item) =>
|
||||||
|
categories.map((cat) => item[cat] || 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Normalize data to percentages
|
||||||
|
const normalizedData = formattedData.map((row) => {
|
||||||
|
const total = row.reduce((sum, val) => sum + val, 0);
|
||||||
|
return row.map((val) => (total > 0 ? (val / total) * 100 : 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate total percentage for each category across all dates
|
||||||
|
const totalData = [];
|
||||||
|
for (let i = 0; i < categories.length; ++i) {
|
||||||
|
let sum = 0;
|
||||||
|
for (let j = 0; j < normalizedData.length; ++j) {
|
||||||
|
sum += normalizedData[j][i];
|
||||||
|
}
|
||||||
|
totalData.push(sum / normalizedData.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define series based on categories with color mapping
|
||||||
|
const series = categories.map((name, idx) => ({
|
||||||
|
name,
|
||||||
|
type: "bar",
|
||||||
|
stack: "total",
|
||||||
|
barWidth: "60%",
|
||||||
|
data: normalizedData.map((row) => row[idx]),
|
||||||
|
itemStyle: {
|
||||||
|
color: colors[idx],
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
valueFormatter: (value) => `${value.toFixed(2)}%`,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Define chart option
|
||||||
|
const option = {
|
||||||
|
grid: {
|
||||||
|
left: "2%",
|
||||||
|
right: "2%",
|
||||||
|
bottom: "10%",
|
||||||
|
top: "5%",
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
axisPointer: {
|
||||||
|
type: "shadow",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: categories,
|
||||||
|
bottom: 0,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: rawAnalystList.map((item) => item.date),
|
||||||
|
axisLabel: {
|
||||||
|
color: "#fff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
max: 100,
|
||||||
|
|
||||||
|
axisLabel: {
|
||||||
|
show: false, // Hide y-axis labels
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series,
|
||||||
|
animation: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
if (data?.getAnalystEstimate?.length !== 0) {
|
if (data?.getAnalystEstimate?.length !== 0) {
|
||||||
index = findIndex(data?.getAnalystEstimate);
|
index = findIndex(data?.getAnalystEstimate);
|
||||||
|
|
||||||
@ -97,6 +196,8 @@
|
|||||||
changeEBITDA = calculateChange(estimatedEbitdaAvg, ebitda);
|
changeEBITDA = calculateChange(estimatedEbitdaAvg, ebitda);
|
||||||
changeEPS = calculateChange(estimatedEpsAvg, eps);
|
changeEPS = calculateChange(estimatedEpsAvg, eps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let optionsData = getPlotOptions() || null;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
@ -258,29 +359,30 @@
|
|||||||
class="flex flex-col justify-between p-1 lg:max-w-[32%] text-white"
|
class="flex flex-col justify-between p-1 lg:max-w-[32%] text-white"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="mb-1 text-xl font-bold">Analyst Ratings</h2>
|
<h2 class="mb-1 text-xl font-bold">Latest Analyst Report</h2>
|
||||||
<p>
|
{#if Object?.keys(data?.getAnalystInsight)?.length > 0}
|
||||||
According to {numOfAnalyst} stock analyst, the rating for GameStop
|
<p>{data?.getAnalystInsight?.insight}</p>
|
||||||
is "{consensusRating}". This means that the analyst believes
|
{:else}
|
||||||
this stock is likely to lead to {[
|
<p>
|
||||||
"Strong Sell",
|
According to {numOfAnalyst} stock analyst, the rating for GameStop
|
||||||
"Sell",
|
is "{consensusRating}". This means that the analyst believes
|
||||||
]?.includes(consensusRating)
|
this stock is likely to lead to {[
|
||||||
? "lower"
|
"Strong Sell",
|
||||||
: ["Strong Buy", "Buy"]?.includes(consensusRating)
|
"Sell",
|
||||||
? "higher"
|
]?.includes(consensusRating)
|
||||||
: "similar"} returns than market as a whole.
|
? "lower"
|
||||||
</p>
|
: ["Strong Buy", "Buy"]?.includes(consensusRating)
|
||||||
|
? "higher"
|
||||||
|
: "similar"} returns than market as a whole.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grow pt-2 md:pt-4 lg:pl-4 lg:pt-0">
|
<div class="grow pt-2 md:pt-4 lg:pl-4 lg:pt-0">
|
||||||
<div class="h-[250px] xs:h-[275px]">
|
<div class="app h-[250px] xs:h-[275px]">
|
||||||
<canvas
|
{#if optionsData !== null}
|
||||||
id="myChart"
|
<Chart {init} options={optionsData} class="chart" />
|
||||||
style="display: block; box-sizing: border-box; height: 275px; width: 728px;"
|
{/if}
|
||||||
width="1092"
|
|
||||||
height="412"
|
|
||||||
></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
<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"
|
class="hide-scroll mb-1 mt-2 overflow-x-auto px-1.5 text-center md:mb-0 md:px-0 lg:mt-2"
|
||||||
@ -563,3 +665,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|||||||
@ -250,23 +250,23 @@
|
|||||||
>
|
>
|
||||||
<thead class="">
|
<thead class="">
|
||||||
<tr class="border-b border-[#27272A]">
|
<tr class="border-b border-[#27272A]">
|
||||||
<td class="text-white font-semibold text-[1rem] text-start"
|
<td class="text-white font-semibold text-sm text-start"
|
||||||
>Analyst</td
|
>Analyst</td
|
||||||
>
|
>
|
||||||
<td class="text-white font-semibold text-[1rem] text-start"
|
<td class="text-white font-semibold text-sm text-start"
|
||||||
>Firm</td
|
>Firm</td
|
||||||
>
|
>
|
||||||
<td class="text-white font-semibold text-[1rem] text-end"
|
<td class="text-white font-semibold text-sm text-end"
|
||||||
>Rating</td
|
>Rating</td
|
||||||
>
|
>
|
||||||
<td class="text-white font-semibold text-[1rem] text-end"
|
<td class="text-white font-semibold text-sm text-end"
|
||||||
>Action</td
|
>Action</td
|
||||||
>
|
>
|
||||||
<td class="text-white font-semibold text-[1rem] text-end"
|
<td class="text-white font-semibold text-sm text-end"
|
||||||
>Price Target</td
|
>Price Target</td
|
||||||
>
|
>
|
||||||
|
|
||||||
<td class="text-white font-semibold text-[1rem] text-end"
|
<td class="text-white font-semibold text-sm text-end"
|
||||||
>Date</td
|
>Date</td
|
||||||
>
|
>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user