update color scheme
This commit is contained in:
parent
990c818727
commit
e6d42b8f9d
@ -68,7 +68,7 @@
|
|||||||
class="stroke-current {score >= 7
|
class="stroke-current {score >= 7
|
||||||
? 'text-[#00FC50]'
|
? 'text-[#00FC50]'
|
||||||
: score >= 4
|
: score >= 4
|
||||||
? 'text-[#FF9E21]'
|
? 'text-[#FBCE3C]'
|
||||||
: 'text-[#FF2F1F]'}"
|
: 'text-[#FF2F1F]'}"
|
||||||
stroke-width="3"
|
stroke-width="3"
|
||||||
stroke-dasharray="100.48"
|
stroke-dasharray="100.48"
|
||||||
|
|||||||
@ -82,7 +82,7 @@
|
|||||||
<div class="fixed z-[100] bottom-8 sm:bottom-10 right-8 sm:right-16">
|
<div class="fixed z-[100] bottom-8 sm:bottom-10 right-8 sm:right-16">
|
||||||
<label
|
<label
|
||||||
for="feedbackInfo"
|
for="feedbackInfo"
|
||||||
class="inline-flex items-center justify-center w-12 h-12 sm:w-full sm:h-10 font-medium bg-gray-700 sm:bg-[#FFEDE5] ml-1 mr-0 sm:mr-2 rounded-full cursor-pointer"
|
class="inline-flex items-center justify-center w-12 h-12 sm:w-full sm:h-10 font-semibold bg-gray-700 sm:bg-[#FBCE3C] ml-1 mr-0 sm:mr-2 rounded-full cursor-pointer"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -111,10 +111,10 @@
|
|||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
for="feedbackInfo"
|
for="feedbackInfo"
|
||||||
class="cursor-pointer modal-backdrop bg-[#fff] bg-opacity-[0.05]"
|
class="cursor-pointer modal-backdrop bg-[#fff] bg-opacity-[0.1]"
|
||||||
></label>
|
></label>
|
||||||
|
|
||||||
<div class="modal-box w-full bg-[#000]">
|
<div class="modal-box w-full bg-[#09090B] border border-gray-600">
|
||||||
<div class="flex flex-row items-center pt-5">
|
<div class="flex flex-row items-center pt-5">
|
||||||
<h1 class="text-white text-xl sm:text-2xl font-bold">
|
<h1 class="text-white text-xl sm:text-2xl font-bold">
|
||||||
Your Feedback matters!
|
Your Feedback matters!
|
||||||
@ -185,10 +185,10 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
on:click={() => sendFeedback()}
|
on:click={() => sendFeedback()}
|
||||||
class="mb-4 px-3 py-2 bg-purple-600 smhover:bg-purple-700 {rating?.length !==
|
class="mb-4 px-3 py-2 bg-[#FBCE3C] sm:hover:bg-[#e8b305] {rating?.length !==
|
||||||
0 && inputValue?.length !== 0
|
0 && inputValue?.length !== 0
|
||||||
? 'opacity-100 cursor-pointer'
|
? 'opacity-100 cursor-pointer'
|
||||||
: 'opacity-60 cursor-not-allowed'} w-11/12 rounded-lg m-auto text-white font-semibold text-md"
|
: 'opacity-60 cursor-not-allowed'} w-11/12 rounded-lg m-auto text-black font-semibold text-md"
|
||||||
>
|
>
|
||||||
Send Feedback
|
Send Feedback
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,13 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { governmentContractComponent, displayCompanyName, stockTicker, screenWidth, getCache, setCache } from '$lib/store';
|
import {
|
||||||
import InfoModal from '$lib/components/InfoModal.svelte';
|
governmentContractComponent,
|
||||||
import { Chart } from 'svelte-echarts';
|
displayCompanyName,
|
||||||
|
stockTicker,
|
||||||
|
screenWidth,
|
||||||
|
getCache,
|
||||||
|
setCache,
|
||||||
|
} from "$lib/store";
|
||||||
|
import InfoModal from "$lib/components/InfoModal.svelte";
|
||||||
|
import { Chart } from "svelte-echarts";
|
||||||
import { abbreviateNumber } from "$lib/utils";
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
|
|
||||||
import { init, use } from 'echarts/core';
|
import { init, use } from "echarts/core";
|
||||||
import { BarChart } from 'echarts/charts';
|
import { BarChart } from "echarts/charts";
|
||||||
import { GridComponent } from 'echarts/components';
|
import { GridComponent } from "echarts/components";
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
import { CanvasRenderer } from "echarts/renderers";
|
||||||
use([BarChart, GridComponent, CanvasRenderer]);
|
use([BarChart, GridComponent, CanvasRenderer]);
|
||||||
|
|
||||||
let isLoaded = false;
|
let isLoaded = false;
|
||||||
@ -15,7 +22,7 @@
|
|||||||
let optionsData;
|
let optionsData;
|
||||||
let avgNumberOfContracts = 0;
|
let avgNumberOfContracts = 0;
|
||||||
let displayMaxContracts = 0;
|
let displayMaxContracts = 0;
|
||||||
let displayYear = 'n/a';
|
let displayYear = "n/a";
|
||||||
let totalAmount;
|
let totalAmount;
|
||||||
let totalContract;
|
let totalContract;
|
||||||
|
|
||||||
@ -31,41 +38,46 @@
|
|||||||
numList?.push(item?.numOfContracts);
|
numList?.push(item?.numOfContracts);
|
||||||
});
|
});
|
||||||
|
|
||||||
totalContract = rawData?.reduce((sum, item) => sum + item?.numOfContracts, 0);
|
totalContract = rawData?.reduce(
|
||||||
|
(sum, item) => sum + item?.numOfContracts,
|
||||||
|
0,
|
||||||
|
);
|
||||||
totalAmount = rawData?.reduce((sum, item) => sum + item?.amount, 0);
|
totalAmount = rawData?.reduce((sum, item) => sum + item?.amount, 0);
|
||||||
|
|
||||||
avgNumberOfContracts = Math.floor(totalContract / rawData?.length);
|
avgNumberOfContracts = Math.floor(totalContract / rawData?.length);
|
||||||
const { year: yearWithMaxContracts, numOfContracts: maxContracts } = rawData?.reduce(
|
const { year: yearWithMaxContracts, numOfContracts: maxContracts } =
|
||||||
(max, contract) => (contract?.numOfContracts > max?.numOfContracts ? contract : max),
|
rawData?.reduce(
|
||||||
rawData?.at(0)
|
(max, contract) =>
|
||||||
);
|
contract?.numOfContracts > max?.numOfContracts ? contract : max,
|
||||||
|
rawData?.at(0),
|
||||||
|
);
|
||||||
displayYear = yearWithMaxContracts;
|
displayYear = yearWithMaxContracts;
|
||||||
displayMaxContracts = maxContracts;
|
displayMaxContracts = maxContracts;
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: "axis",
|
||||||
hideDelay: 100, // Set the delay in milliseconds
|
hideDelay: 100, // Set the delay in milliseconds
|
||||||
},
|
},
|
||||||
animation: false,
|
animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
left: '2%',
|
left: "2%",
|
||||||
right: $screenWidth < 640 ? '0%' : '2%',
|
right: $screenWidth < 640 ? "0%" : "2%",
|
||||||
bottom: '0%',
|
bottom: "0%",
|
||||||
top: '10%',
|
top: "10%",
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
data: dates,
|
data: dates,
|
||||||
type: 'category',
|
type: "category",
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: '#fff',
|
color: "#fff",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: "value",
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: false, // Disable x-axis grid lines
|
show: false, // Disable x-axis grid lines
|
||||||
},
|
},
|
||||||
@ -74,32 +86,32 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: "value",
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: false, // Disable x-axis grid lines
|
show: false, // Disable x-axis grid lines
|
||||||
},
|
},
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
show: false, // Hide y-axis labels
|
show: false, // Hide y-axis labels
|
||||||
},
|
},
|
||||||
position: 'right',
|
position: "right",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '# of Contracts',
|
name: "# of Contracts",
|
||||||
data: numList,
|
data: numList,
|
||||||
type: 'line',
|
type: "line",
|
||||||
yAxisIndex: 1,
|
yAxisIndex: 1,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#fff', // Change bar color to white
|
color: "#fff", // Change bar color to white
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Amount',
|
name: "Amount",
|
||||||
data: amountList,
|
data: amountList,
|
||||||
type: 'bar',
|
type: "bar",
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#FF9E21', // Change bar color to orange
|
color: "#FBCE3C", // Change bar color to orange
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -109,27 +121,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getGovernmentContract = async (ticker) => {
|
const getGovernmentContract = async (ticker) => {
|
||||||
const cachedData = getCache(ticker, 'getGovernmentContract');
|
const cachedData = getCache(ticker, "getGovernmentContract");
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
rawData = cachedData;
|
rawData = cachedData;
|
||||||
} else {
|
} else {
|
||||||
const postData = { ticker: ticker, path: 'government-contract' };
|
const postData = { ticker: ticker, path: "government-contract" };
|
||||||
const response = await fetch('/api/ticker-data', {
|
const response = await fetch("/api/ticker-data", {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(postData),
|
body: JSON.stringify(postData),
|
||||||
});
|
});
|
||||||
rawData = await response?.json();
|
rawData = await response?.json();
|
||||||
setCache(ticker, rawData, 'getGovernmentContract');
|
setCache(ticker, rawData, "getGovernmentContract");
|
||||||
}
|
}
|
||||||
|
|
||||||
governmentContractComponent.set(rawData?.length !== 0);
|
governmentContractComponent.set(rawData?.length !== 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if ($stockTicker && typeof window !== 'undefined') {
|
if ($stockTicker && typeof window !== "undefined") {
|
||||||
isLoaded = false;
|
isLoaded = false;
|
||||||
const ticker = $stockTicker;
|
const ticker = $stockTicker;
|
||||||
const asyncFunctions = [getGovernmentContract(ticker)];
|
const asyncFunctions = [getGovernmentContract(ticker)];
|
||||||
@ -141,104 +153,114 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('An error occurred:', error);
|
console.error("An error occurred:", error);
|
||||||
});
|
});
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<section class="overflow-hidden text-white h-full pb-8">
|
||||||
|
<main class="overflow-hidden">
|
||||||
|
<div class="flex flex-row items-center">
|
||||||
|
<label
|
||||||
|
for="governmentContractInfo"
|
||||||
|
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
|
||||||
|
>
|
||||||
|
US Government Contract
|
||||||
|
</label>
|
||||||
|
<InfoModal
|
||||||
|
title={"Government Contract"}
|
||||||
|
content={"Government contracts are agreements between the local government and companies for goods or services. They can be substantial revenue sources for companies, particularly in sectors like defense, technology, and infrastructure. Winning contracts can enhance a company's stability and credibility, but it often involves competitive bidding and compliance with strict regulations."}
|
||||||
|
id={"governmentContractInfo"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if isLoaded}
|
||||||
<section class="overflow-hidden text-white h-full pb-8">
|
{#if rawData?.length !== 0}
|
||||||
<main class="overflow-hidden ">
|
|
||||||
|
|
||||||
<div class="flex flex-row items-center">
|
|
||||||
<label for="governmentContractInfo" class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold">
|
|
||||||
US Government Contract
|
|
||||||
</label>
|
|
||||||
<InfoModal
|
|
||||||
title={"Government Contract"}
|
|
||||||
content={"Government contracts are agreements between the local government and companies for goods or services. They can be substantial revenue sources for companies, particularly in sectors like defense, technology, and infrastructure. Winning contracts can enhance a company's stability and credibility, but it often involves competitive bidding and compliance with strict regulations."}
|
|
||||||
id={"governmentContractInfo"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if isLoaded}
|
|
||||||
|
|
||||||
{#if rawData?.length !== 0}
|
|
||||||
|
|
||||||
<div class="w-full flex flex-col items-start">
|
<div class="w-full flex flex-col items-start">
|
||||||
<div class="text-white text-sm sm:text-[1rem] mt-2 mb-2 w-full">
|
<div class="text-white text-sm sm:text-[1rem] mt-2 mb-2 w-full">
|
||||||
Since 2015, {$displayCompanyName} has secured a total of {totalContract} government contracts, amassing {abbreviateNumber(totalAmount,true)} in revenue. The company has averaged {avgNumberOfContracts} contracts per year, with a peak of {displayMaxContracts} contracts in {displayYear}.
|
Since 2015, {$displayCompanyName} has secured a total of {totalContract}
|
||||||
</div>
|
government contracts, amassing {abbreviateNumber(totalAmount, true)}
|
||||||
|
in revenue. The company has averaged {avgNumberOfContracts} contracts
|
||||||
|
per year, with a peak of {displayMaxContracts} contracts in {displayYear}.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pb-2 rounded-lg bg-[#09090B]">
|
<div class="pb-2 rounded-lg bg-[#09090B]">
|
||||||
|
|
||||||
|
|
||||||
<div class="app w-full h-[300px] mt-5">
|
<div class="app w-full h-[300px] mt-5">
|
||||||
<Chart {init} options={optionsData} class="chart" />
|
<Chart {init} options={optionsData} class="chart" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="h-full transform -translate-x-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<span
|
||||||
|
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
|
||||||
|
>
|
||||||
|
# of Contracts
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="h-full transform -translate-x-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="w-3 h-3 bg-[#FFAD24] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<span
|
||||||
|
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
|
||||||
|
>
|
||||||
|
Amount
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
<div class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12">
|
{:else}
|
||||||
|
<div class="flex justify-center items-center h-80">
|
||||||
<div class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center">
|
<div class="relative">
|
||||||
<div class="h-full transform -translate-x-1/2 " aria-hidden="true"></div>
|
<label
|
||||||
<div class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2" aria-hidden="true"></div>
|
class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
<span class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block">
|
>
|
||||||
# of Contracts
|
<span class="loading loading-spinner loading-md text-gray-400"
|
||||||
</span>
|
></span>
|
||||||
</div>
|
</label>
|
||||||
|
|
||||||
<div class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center">
|
|
||||||
<div class="h-full transform -translate-x-1/2 " aria-hidden="true"></div>
|
|
||||||
<div class="w-3 h-3 bg-[#FFAD24] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2" aria-hidden="true"></div>
|
|
||||||
<span class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block">
|
|
||||||
Amount
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</main>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{:else}
|
|
||||||
<div class="flex justify-center items-center h-80">
|
|
||||||
<div class="relative">
|
|
||||||
<label class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
|
||||||
<span class="loading loading-spinner loading-md text-gray-400"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
|
|
||||||
</main>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
max-width: 100%; /* Ensure chart width doesn't exceed the container */
|
max-width: 100%; /* Ensure chart width doesn't exceed the container */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.app {
|
.app {
|
||||||
height: 210px;
|
height: 210px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart {
|
.chart {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,28 +1,33 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { abbreviateNumber } from "$lib/utils";
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
import { swapComponent, stockTicker, screenWidth, getCache, setCache } from '$lib/store';
|
import {
|
||||||
import InfoModal from '$lib/components/InfoModal.svelte';
|
swapComponent,
|
||||||
import { Chart } from 'svelte-echarts';
|
stockTicker,
|
||||||
import { init, use } from 'echarts/core';
|
screenWidth,
|
||||||
import { ScatterChart } from 'echarts/charts';
|
getCache,
|
||||||
import { GridComponent, TooltipComponent } from 'echarts/components';
|
setCache,
|
||||||
import { CanvasRenderer } from 'echarts/renderers';
|
} from "$lib/store";
|
||||||
|
import InfoModal from "$lib/components/InfoModal.svelte";
|
||||||
|
import { Chart } from "svelte-echarts";
|
||||||
|
import { init, use } from "echarts/core";
|
||||||
|
import { ScatterChart } from "echarts/charts";
|
||||||
|
import { GridComponent, TooltipComponent } from "echarts/components";
|
||||||
|
import { CanvasRenderer } from "echarts/renderers";
|
||||||
export let data: any; // Add type for `data` if possible
|
export let data: any; // Add type for `data` if possible
|
||||||
|
|
||||||
use([ScatterChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
use([ScatterChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
||||||
|
|
||||||
let isLoaded = false;
|
let isLoaded = false;
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [{ title: "Effective Date" }, { title: "Expiration Date" }];
|
||||||
{ title: "Effective Date" },
|
|
||||||
{ title: "Expiration Date" },
|
|
||||||
];
|
|
||||||
|
|
||||||
let activeIdx = 0;
|
let activeIdx = 0;
|
||||||
|
|
||||||
function changeTab(index: number) {
|
function changeTab(index: number) {
|
||||||
activeIdx = index;
|
activeIdx = index;
|
||||||
optionsData = getPlotOptions(activeIdx === 0 ? 'effectiveDate' : 'expirationDate');
|
optionsData = getPlotOptions(
|
||||||
|
activeIdx === 0 ? "effectiveDate" : "expirationDate",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rawData: any[] = [];
|
let rawData: any[] = [];
|
||||||
@ -30,71 +35,84 @@
|
|||||||
let avgNotionalAmount: number | undefined;
|
let avgNotionalAmount: number | undefined;
|
||||||
let avgNotionalQuantity: number | undefined;
|
let avgNotionalQuantity: number | undefined;
|
||||||
|
|
||||||
|
function getPlotOptions(state: "effectiveDate" | "expirationDate") {
|
||||||
function getPlotOptions(state: 'effectiveDate' | 'expirationDate') {
|
|
||||||
const combinedData = rawData?.map((item) => ({
|
const combinedData = rawData?.map((item) => ({
|
||||||
date: state === 'effectiveDate' ? item['Effective Date'] : item['Expiration Date'],
|
date:
|
||||||
notionalAmount: item['Notional amount-Leg 1'],
|
state === "effectiveDate"
|
||||||
notionalQuantity: item['Total notional quantity-Leg 1'],
|
? item["Effective Date"]
|
||||||
|
: item["Expiration Date"],
|
||||||
|
notionalAmount: item["Notional amount-Leg 1"],
|
||||||
|
notionalQuantity: item["Total notional quantity-Leg 1"],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Group data by date and sum the values
|
// Group data by date and sum the values
|
||||||
const groupedData = combinedData.reduce((acc, curr) => {
|
const groupedData = combinedData.reduce(
|
||||||
const { date, notionalAmount, notionalQuantity } = curr;
|
(acc, curr) => {
|
||||||
|
const { date, notionalAmount, notionalQuantity } = curr;
|
||||||
|
|
||||||
if (acc[date]) {
|
if (acc[date]) {
|
||||||
acc[date].notionalAmount += notionalAmount;
|
acc[date].notionalAmount += notionalAmount;
|
||||||
acc[date].notionalQuantity += notionalQuantity;
|
acc[date].notionalQuantity += notionalQuantity;
|
||||||
} else {
|
} else {
|
||||||
acc[date] = {
|
acc[date] = {
|
||||||
date,
|
date,
|
||||||
notionalAmount,
|
notionalAmount,
|
||||||
notionalQuantity,
|
notionalQuantity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, any>);
|
},
|
||||||
|
{} as Record<string, any>,
|
||||||
|
);
|
||||||
|
|
||||||
const result = Object.values(groupedData);
|
const result = Object.values(groupedData);
|
||||||
|
|
||||||
result?.sort((a, b) => new Date(a?.date).getTime() - new Date(b?.date).getTime());
|
result?.sort(
|
||||||
|
(a, b) => new Date(a?.date).getTime() - new Date(b?.date).getTime(),
|
||||||
|
);
|
||||||
|
|
||||||
const dates = result?.map((item) => item?.date);
|
const dates = result?.map((item) => item?.date);
|
||||||
const notionalAmount = result?.map((item) => item?.notionalAmount);
|
const notionalAmount = result?.map((item) => item?.notionalAmount);
|
||||||
const notionalQuantity = result?.map((item) => item?.notionalQuantity);
|
const notionalQuantity = result?.map((item) => item?.notionalQuantity);
|
||||||
|
|
||||||
const totalNotionalAmount = notionalAmount?.reduce((acc, item) => acc + item, 0);
|
const totalNotionalAmount = notionalAmount?.reduce(
|
||||||
|
(acc, item) => acc + item,
|
||||||
|
0,
|
||||||
|
);
|
||||||
avgNotionalAmount = totalNotionalAmount / notionalAmount?.length;
|
avgNotionalAmount = totalNotionalAmount / notionalAmount?.length;
|
||||||
|
|
||||||
const totalNotionalQuantity = notionalQuantity?.reduce((acc, item) => acc + item, 0);
|
const totalNotionalQuantity = notionalQuantity?.reduce(
|
||||||
|
(acc, item) => acc + item,
|
||||||
|
0,
|
||||||
|
);
|
||||||
avgNotionalQuantity = totalNotionalQuantity / notionalQuantity?.length;
|
avgNotionalQuantity = totalNotionalQuantity / notionalQuantity?.length;
|
||||||
|
|
||||||
const option = {
|
const option = {
|
||||||
silent: true,
|
silent: true,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: "axis",
|
||||||
hideDelay: 100,
|
hideDelay: 100,
|
||||||
},
|
},
|
||||||
animation: false,
|
animation: false,
|
||||||
grid: {
|
grid: {
|
||||||
left: '3%',
|
left: "3%",
|
||||||
right: '3%',
|
right: "3%",
|
||||||
bottom: '0%',
|
bottom: "0%",
|
||||||
top: '10%',
|
top: "10%",
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: "category",
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: dates,
|
data: dates,
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
color: '#fff',
|
color: "#fff",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
yAxis: [
|
yAxis: [
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: "value",
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@ -103,11 +121,11 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'value',
|
type: "value",
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
position: 'right',
|
position: "right",
|
||||||
axisLabel: {
|
axisLabel: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@ -115,20 +133,20 @@
|
|||||||
],
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: 'Notional Amount',
|
name: "Notional Amount",
|
||||||
type: 'scatter',
|
type: "scatter",
|
||||||
data: dates?.map((date, index) => [date, notionalAmount[index]]),
|
data: dates?.map((date, index) => [date, notionalAmount[index]]),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#8F54F4',
|
color: "#8F54F4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Notional Quantity',
|
name: "Notional Quantity",
|
||||||
type: 'scatter',
|
type: "scatter",
|
||||||
data: dates?.map((date, index) => [date, notionalQuantity[index]]),
|
data: dates?.map((date, index) => [date, notionalQuantity[index]]),
|
||||||
yAxisIndex: 1,
|
yAxisIndex: 1,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: '#FF9E21',
|
color: "#FBCE3C",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -138,35 +156,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getSwapData(ticker: string) {
|
async function getSwapData(ticker: string) {
|
||||||
const cachedData = getCache(ticker, 'getSwapData');
|
const cachedData = getCache(ticker, "getSwapData");
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
rawData = cachedData;
|
rawData = cachedData;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const postData = { ticker, path: 'swap-ticker' };
|
const postData = { ticker, path: "swap-ticker" };
|
||||||
const response = await fetch('/api/ticker-data', {
|
const response = await fetch("/api/ticker-data", {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(postData),
|
body: JSON.stringify(postData),
|
||||||
});
|
});
|
||||||
if (!response.ok) throw new Error('API request failed');
|
if (!response.ok) throw new Error("API request failed");
|
||||||
rawData = await response.json();
|
rawData = await response.json();
|
||||||
setCache(ticker, rawData, 'getSwapData');
|
setCache(ticker, rawData, "getSwapData");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch swap data:', error);
|
console.error("Failed to fetch swap data:", error);
|
||||||
rawData = [];
|
rawData = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$swapComponent = rawData?.length !== 0; // Correct the use of `$`
|
$swapComponent = rawData?.length !== 0; // Correct the use of `$`
|
||||||
}
|
}
|
||||||
|
|
||||||
$: if ($stockTicker && typeof window !== 'undefined') {
|
$: if ($stockTicker && typeof window !== "undefined") {
|
||||||
isLoaded = false;
|
isLoaded = false;
|
||||||
activeIdx = 0;
|
activeIdx = 0;
|
||||||
getSwapData($stockTicker).then(() => {
|
getSwapData($stockTicker).then(() => {
|
||||||
optionsData = getPlotOptions('effectiveDate');
|
optionsData = getPlotOptions("effectiveDate");
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -174,119 +192,129 @@
|
|||||||
$: charNumber = $screenWidth < 640 ? 20 : 40;
|
$: charNumber = $screenWidth < 640 ? 20 : 40;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<section class="overflow-hidden text-white h-full pb-8">
|
||||||
|
<main class="overflow-hidden">
|
||||||
|
<div class="flex flex-row items-center">
|
||||||
|
<label
|
||||||
|
for="swapInfo"
|
||||||
|
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
|
||||||
|
>
|
||||||
|
Swap Data
|
||||||
|
</label>
|
||||||
|
<InfoModal
|
||||||
|
title={"Swap Data"}
|
||||||
|
content={"Swaps in the stock market are derivative contracts to exchange cash flows or assets, used for risk management, speculation, and enhancing market liquidity."}
|
||||||
|
id={"swapInfo"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if isLoaded}
|
||||||
|
{#if rawData?.length !== 0}
|
||||||
<section class="overflow-hidden text-white h-full pb-8">
|
|
||||||
<main class="overflow-hidden ">
|
|
||||||
|
|
||||||
<div class="flex flex-row items-center">
|
|
||||||
<label for="swapInfo" class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold">
|
|
||||||
Swap Data
|
|
||||||
</label>
|
|
||||||
<InfoModal
|
|
||||||
title={"Swap Data"}
|
|
||||||
content={"Swaps in the stock market are derivative contracts to exchange cash flows or assets, used for risk management, speculation, and enhancing market liquidity."}
|
|
||||||
id={"swapInfo"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if isLoaded}
|
|
||||||
|
|
||||||
{#if rawData?.length !== 0}
|
|
||||||
|
|
||||||
<div class="w-full flex flex-col items-start">
|
<div class="w-full flex flex-col items-start">
|
||||||
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
|
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
|
||||||
The swap data from the past 100 days shows an average notional amount of {abbreviateNumber(avgNotionalAmount,true)} and an average notional quantity of {abbreviateNumber(avgNotionalQuantity)}.
|
The swap data from the past 100 days shows an average notional
|
||||||
</div>
|
amount of {abbreviateNumber(avgNotionalAmount, true)} and an average
|
||||||
|
notional quantity of {abbreviateNumber(avgNotionalQuantity)}.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pb-2 rounded-lg bg-[#09090B]">
|
<div class="pb-2 rounded-lg bg-[#09090B]">
|
||||||
|
<div
|
||||||
<div class="bg-[#313131] w-fit relative flex flex-wrap items-center justify-center rounded-lg p-1 mt-4">
|
class="bg-[#313131] w-fit relative flex flex-wrap items-center justify-center rounded-lg p-1 mt-4"
|
||||||
{#each tabs as item, i}
|
>
|
||||||
<button
|
{#each tabs as item, i}
|
||||||
on:click={() => changeTab(i)}
|
<button
|
||||||
class="group relative z-[1] rounded-full px-3 py-1 {activeIdx === i
|
on:click={() => changeTab(i)}
|
||||||
? 'z-0'
|
class="group relative z-[1] rounded-full px-3 py-1 {activeIdx ===
|
||||||
: ''} ">
|
i
|
||||||
{#if activeIdx === i}
|
? 'z-0'
|
||||||
<div
|
: ''} "
|
||||||
class="absolute inset-0 rounded-lg bg-purple-600"
|
>
|
||||||
></div>
|
{#if activeIdx === i}
|
||||||
{/if}
|
<div class="absolute inset-0 rounded-lg bg-purple-600"></div>
|
||||||
<span
|
{/if}
|
||||||
class="relative text-sm block font-medium duration-200 text-white">
|
<span
|
||||||
{item.title}
|
class="relative text-sm block font-medium duration-200 text-white"
|
||||||
</span>
|
>
|
||||||
</button>
|
{item.title}
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="app w-full h-[300px] mt-5">
|
|
||||||
<Chart {init} options={optionsData} class="chart" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12">
|
|
||||||
|
|
||||||
<div class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center">
|
|
||||||
<div class="h-full transform -translate-x-1/2 " aria-hidden="true"></div>
|
|
||||||
<div class="w-3 h-3 bg-[#8F54F4] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2" aria-hidden="true"></div>
|
|
||||||
<span class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block">
|
|
||||||
Notional Amount
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</button>
|
||||||
|
{/each}
|
||||||
<div class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center">
|
</div>
|
||||||
<div class="h-full transform -translate-x-1/2 " aria-hidden="true"></div>
|
|
||||||
<div class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2" aria-hidden="true"></div>
|
|
||||||
<span class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block">
|
|
||||||
Notional Quantity
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="app w-full h-[300px] mt-5">
|
||||||
|
<Chart {init} options={optionsData} class="chart" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="h-full transform -translate-x-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="w-3 h-3 bg-[#8F54F4] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
|
||||||
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<span
|
||||||
|
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
|
||||||
|
>
|
||||||
|
Notional Amount
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
{/if}
|
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
|
||||||
|
>
|
||||||
{:else}
|
<div
|
||||||
<div class="flex justify-center items-center h-80">
|
class="h-full transform -translate-x-1/2"
|
||||||
<div class="relative">
|
aria-hidden="true"
|
||||||
<label class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
></div>
|
||||||
<span class="loading loading-spinner loading-md text-gray-400"></span>
|
<div
|
||||||
</label>
|
class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
|
||||||
</div>
|
aria-hidden="true"
|
||||||
|
></div>
|
||||||
|
<span
|
||||||
|
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
|
||||||
|
>
|
||||||
|
Notional Quantity
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<div class="flex justify-center items-center h-80">
|
||||||
</main>
|
<div class="relative">
|
||||||
</section>
|
<label
|
||||||
|
class="bg-[#09090B] rounded-xl h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||||
|
>
|
||||||
|
<span class="loading loading-spinner loading-md text-gray-400"
|
||||||
|
></span>
|
||||||
<style>
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</main>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
.app {
|
.app {
|
||||||
height: 300px;
|
height: 300px;
|
||||||
max-width: 100%; /* Ensure chart width doesn't exceed the container */
|
max-width: 100%; /* Ensure chart width doesn't exceed the container */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.app {
|
.app {
|
||||||
height: 210px;
|
height: 210px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart {
|
.chart {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
</style>
|
|
||||||
|
|||||||
@ -408,7 +408,7 @@
|
|||||||
type: "bar",
|
type: "bar",
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#FF9E21", // Change bar color to white
|
color: "#FBCE3C", // Change bar color to white
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -97,7 +97,7 @@
|
|||||||
|
|
||||||
// Define categories in the exact order you specified
|
// Define categories in the exact order you specified
|
||||||
const categories = ["Strong Sell", "Sell", "Hold", "Buy", "Strong Buy"];
|
const categories = ["Strong Sell", "Sell", "Hold", "Buy", "Strong Buy"];
|
||||||
const colors = ["#9E190A", "#D9220E", "#FF9E21", "#31B800", "#008A00"];
|
const colors = ["#9E190A", "#D9220E", "#FBCE3C", "#31B800", "#008A00"];
|
||||||
|
|
||||||
// Create a consistent mapping for data
|
// Create a consistent mapping for data
|
||||||
const formattedData = rawAnalystList.map((item) =>
|
const formattedData = rawAnalystList.map((item) =>
|
||||||
@ -216,7 +216,7 @@
|
|||||||
color: [
|
color: [
|
||||||
[0.2, "#9E190A"],
|
[0.2, "#9E190A"],
|
||||||
[0.4, "#D9220E"],
|
[0.4, "#D9220E"],
|
||||||
[0.6, "#FF9E21"],
|
[0.6, "#FBCE3C"],
|
||||||
[0.8, "#31B800"],
|
[0.8, "#31B800"],
|
||||||
[1, "#008A00"],
|
[1, "#008A00"],
|
||||||
],
|
],
|
||||||
@ -409,7 +409,7 @@
|
|||||||
symbol: "none",
|
symbol: "none",
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
type: "dashed",
|
type: "dashed",
|
||||||
color: "#FF9E21",
|
color: "#FBCE3C",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -533,7 +533,7 @@
|
|||||||
? 'text-[#00FC50]'
|
? 'text-[#00FC50]'
|
||||||
: ['Strong Sell', 'Sell']?.includes(consensusRating)
|
: ['Strong Sell', 'Sell']?.includes(consensusRating)
|
||||||
? 'text-[#FF2F1F]'
|
? 'text-[#FF2F1F]'
|
||||||
: 'text-[#FF9E21]'}">{consensusRating}</span
|
: 'text-[#FBCE3C]'}">{consensusRating}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user