Add GovernmentContract component
This commit is contained in:
parent
a43774f6d0
commit
a0ac1fb7ed
@ -1,54 +1,95 @@
|
||||
|
||||
<script lang ='ts'>
|
||||
import { displayCompanyName, stockTicker, screenWidth} from '$lib/store';
|
||||
import { governmentContractComponent, displayCompanyName, stockTicker, screenWidth, userRegion, getCache, setCache} from '$lib/store';
|
||||
import InfoModal from '$lib/components/InfoModal.svelte';
|
||||
import { Chart } from 'svelte-echarts'
|
||||
|
||||
import { abbreviateNumber, formatString } from "$lib/utils";
|
||||
|
||||
import Lazy from 'svelte-lazy';
|
||||
|
||||
|
||||
export let contractList;
|
||||
|
||||
export let data;
|
||||
|
||||
let isLoaded = false;
|
||||
const usRegion = ['cle1','iad1','pdx1','sfo1'];
|
||||
|
||||
let apiURL;
|
||||
|
||||
userRegion.subscribe(value => {
|
||||
|
||||
if (usRegion.includes(value)) {
|
||||
apiURL = import.meta.env.VITE_USEAST_API_URL;
|
||||
} else {
|
||||
apiURL = import.meta.env.VITE_EU_API_URL;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let rawData = [];
|
||||
let optionsData;
|
||||
let avgNumberOfContracts = 0;
|
||||
let displayMaxContracts = 0;
|
||||
let displayYear = 'n/a';
|
||||
let totalAmount;
|
||||
let totalContract;
|
||||
|
||||
function normalizer(value) {
|
||||
if (Math?.abs(value) >= 1e9) {
|
||||
|
||||
function normalizer(value) {
|
||||
if (Math?.abs(value) >= 1e18) {
|
||||
return { unit: 'Q', denominator: 1e18 };
|
||||
} else if (Math?.abs(value) >= 1e12) {
|
||||
return { unit: 'T', denominator: 1e12 };
|
||||
} else if (Math?.abs(value) >= 1e9) {
|
||||
return { unit: 'B', denominator: 1e9 };
|
||||
} else if (Math?.abs(value) >= 1e6) {
|
||||
return { unit: 'M', denominator: 1e6 };
|
||||
} else if (Math?.abs(value) >= 1e5) {
|
||||
return { unit: 'K', denominator: 1e5 };
|
||||
} else if (Math?.abs(value) >= 1e4) {
|
||||
return { unit: 'K', denominator: 1e4 };
|
||||
} else {
|
||||
return { unit: '', denominator: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getPlotOptions() {
|
||||
}
|
||||
|
||||
|
||||
function getPlotOptions() {
|
||||
let dates = [];
|
||||
let valueList = [];
|
||||
let amountList = [];
|
||||
let numList = []
|
||||
|
||||
// Iterate over the data and extract required information
|
||||
contractList?.forEach(item => {
|
||||
rawData?.forEach(item => {
|
||||
// Extract year and convert it to fiscal year format
|
||||
const fiscalYear = "FY" + item?.year?.slice(2);
|
||||
dates?.push(fiscalYear);
|
||||
|
||||
// Extract totalValue
|
||||
valueList?.push(item.totalValue);
|
||||
});
|
||||
amountList?.push(item?.amount);
|
||||
numList?.push(item?.numOfContracts);
|
||||
|
||||
const {unit, denominator } = normalizer(Math.max(...valueList) ?? 0)
|
||||
});
|
||||
|
||||
// Calculate total number of contracts
|
||||
totalContract = rawData?.reduce((sum, item) => sum + item?.numOfContracts, 0)
|
||||
totalAmount = rawData?.reduce((sum, item) => sum + item?.amount, 0);
|
||||
|
||||
avgNumberOfContracts = Math.floor((totalContract)/rawData?.length);
|
||||
const { year:yearWithMaxContracts, numOfContracts:maxContracts } = rawData?.reduce((max, contract) => contract?.numOfContracts > max?.numOfContracts ? contract : max, rawData?.at(0));
|
||||
displayYear = yearWithMaxContracts;
|
||||
displayMaxContracts = maxContracts
|
||||
|
||||
const {unit, denominator } = normalizer(Math.max(...amountList) ?? 0)
|
||||
|
||||
const option = {
|
||||
silent: true,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
hideDelay: 100, // Set the delay in milliseconds
|
||||
},
|
||||
animation: $screenWidth < 640 ? false: true,
|
||||
grid: {
|
||||
left: $screenWidth < 640 ? '0%' : '2%',
|
||||
right: $screenWidth < 640 ? '5%' : '2%',
|
||||
bottom: $screenWidth < 640 ? '0%' : '2%',
|
||||
left: '2%',
|
||||
right: '4%',
|
||||
bottom: '0%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
@ -63,107 +104,217 @@ function getPlotOptions() {
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#6E7079', // Change label color to white
|
||||
formatter: function (value) {
|
||||
return '$'+(value / denominator)?.toFixed(1) + unit; // Format value in millions
|
||||
formatter: function (value, index) {
|
||||
if(index % 2) {
|
||||
return '$'+(value / denominator)?.toFixed(1) + unit; // Format value in millions
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false, // Disable x-axis grid lines
|
||||
},
|
||||
position: 'right',
|
||||
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
data: valueList,
|
||||
name: '# of Contracts',
|
||||
data: numList,
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
itemStyle: {
|
||||
color: '#fff' // Change bar color to white
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Amount',
|
||||
data: amountList,
|
||||
type: 'bar',
|
||||
itemStyle: {
|
||||
color: 'rgb(255,255,255,0.8)' // Change bar color to white
|
||||
}
|
||||
color: '#FF9E21' // Change bar color to white
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
$: {
|
||||
if($stockTicker && typeof window !== 'undefined' && contractList?.length !== 0) {
|
||||
|
||||
optionsData = getPlotOptions()
|
||||
|
||||
// Calculate total number of contracts
|
||||
avgNumberOfContracts = Math.floor((contractList?.reduce((sum, contract) => sum + contract?.numOfContracts, 0))/contractList?.length);
|
||||
const { year:yearWithMaxContracts, numOfContracts:maxContracts } = contractList?.reduce((max, contract) => contract?.numOfContracts > max?.numOfContracts ? contract : max, contractList?.at(0));
|
||||
displayYear = yearWithMaxContracts;
|
||||
displayMaxContracts = maxContracts
|
||||
|
||||
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
const getGovernmentContract = async (ticker) => {
|
||||
// Get cached data for the specific tickerID
|
||||
const cachedData = getCache(ticker, 'getGovernmentContract');
|
||||
if (cachedData) {
|
||||
rawData = cachedData;
|
||||
} else {
|
||||
|
||||
const postData = {'ticker': ticker};
|
||||
// make the POST request to the endpoint
|
||||
const response = await fetch(apiURL + '/government-contract', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(postData)
|
||||
});
|
||||
|
||||
rawData = (await response.json());
|
||||
// Cache the data for this specific tickerID with a specific name 'getGovernmentContract'
|
||||
setCache(ticker, rawData, 'getGovernmentContract');
|
||||
}
|
||||
|
||||
if(rawData?.length !== 0) {
|
||||
$governmentContractComponent = true;
|
||||
} else {
|
||||
$governmentContractComponent = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
$: {
|
||||
if($stockTicker && typeof window !== 'undefined') {
|
||||
isLoaded=false;
|
||||
const ticker = $stockTicker
|
||||
const asyncFunctions = [
|
||||
getGovernmentContract(ticker)
|
||||
];
|
||||
Promise.all(asyncFunctions)
|
||||
.then((results) => {
|
||||
if(rawData?.length !== 0) {
|
||||
optionsData = getPlotOptions();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('An error occurred:', error);
|
||||
});
|
||||
isLoaded = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
let charNumber = 20;
|
||||
|
||||
$: {
|
||||
if($screenWidth < 640)
|
||||
{
|
||||
charNumber = 20;
|
||||
}
|
||||
else {
|
||||
charNumber =40;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<section class="overflow-hidden text-white h-full pb-8 sm:pb-2 ">
|
||||
<main class="overflow-hidden ">
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<label for="governmentContractsInfo" class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold">
|
||||
Government Contracts
|
||||
</label>
|
||||
<InfoModal
|
||||
title={"Government Contracts"}
|
||||
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={"governmentContractsInfo"}
|
||||
/>
|
||||
<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 data?.user?.tier === 'Pro'}
|
||||
{#if isLoaded}
|
||||
|
||||
{#if rawData?.length !== 0}
|
||||
|
||||
<div class="w-full flex flex-col items-start">
|
||||
<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}.
|
||||
</div>
|
||||
|
||||
{#if contractList?.length !== 0}
|
||||
<div class="w-full flex flex-col items-start">
|
||||
<div class="text-white text-sm sm:text-[1rem] mt-1 sm:mt-3 mb-1 w-full">
|
||||
Gain insights into government spending on {$displayCompanyName} and determine the annual value of significant government contracts awarded to the company.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pb-2 rounded-lg bg-[#0F0F0F]">
|
||||
|
||||
|
||||
<Lazy height={300} fadeOption={{delay: 100, duration: 500}} keep={true}>
|
||||
<div class="app w-full h-[300px] ">
|
||||
<div class="app w-full h-[300px] mt-5">
|
||||
<Chart options={optionsData} class="chart" />
|
||||
</div>
|
||||
</Lazy>
|
||||
|
||||
<div class="w-full text-white text-sm sm:text-[1rem] mt-6">
|
||||
The company averaged {avgNumberOfContracts} contracts annually, peaking at {displayMaxContracts} in {displayYear}.
|
||||
</div>
|
||||
|
||||
{:else}
|
||||
<h2 class="mt-10 mb-5 flex justify-center items-center text-3xl font-bold text-slate-700 m-auto">
|
||||
No data available
|
||||
<svg class="w-10 sm:w-12 inline-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#334155" d="M18.68 12.32a4.49 4.49 0 0 0-6.36.01a4.49 4.49 0 0 0 0 6.36a4.508 4.508 0 0 0 5.57.63L21 22.39L22.39 21l-3.09-3.11c1.13-1.77.87-4.09-.62-5.57m-1.41 4.95c-.98.98-2.56.97-3.54 0c-.97-.98-.97-2.56.01-3.54c.97-.97 2.55-.97 3.53 0c.97.98.97 2.56 0 3.54M10.9 20.1a6.527 6.527 0 0 1-1.48-2.32C6.27 17.25 4 15.76 4 14v3c0 2.21 3.58 4 8 4c-.4-.26-.77-.56-1.1-.9M4 9v3c0 1.68 2.07 3.12 5 3.7v-.2c0-.93.2-1.85.58-2.69C6.34 12.3 4 10.79 4 9m8-6C7.58 3 4 4.79 4 7c0 2 3 3.68 6.85 4h.05c1.2-1.26 2.86-2 4.6-2c.91 0 1.81.19 2.64.56A3.215 3.215 0 0 0 20 7c0-2.21-3.58-4-8-4Z"/></svg>
|
||||
</h2>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12">
|
||||
|
||||
{/if}
|
||||
|
||||
</main>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<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-[#202020] 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>
|
||||
|
||||
<style>
|
||||
<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-[#202020] 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>
|
||||
|
||||
|
||||
.app {
|
||||
height: 300px;
|
||||
max-width: 100%; /* Ensure chart width doesn't exceed the container */
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.app {
|
||||
height: 230px;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
{/if}
|
||||
|
||||
{:else}
|
||||
<div class="flex justify-center items-center h-80">
|
||||
<div class="relative">
|
||||
<label class="bg-[#202020] 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"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{:else}
|
||||
<div class="shadow-lg shadow-bg-[#000] bg-[#202020] sm:bg-opacity-[0.5] text-sm sm:text-[1rem] rounded-md w-full p-4 min-h-24 mt-4 text-white m-auto flex justify-center items-center text-center font-semibold">
|
||||
<svg class="mr-1.5 w-5 h-5 inline-block"xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#A3A3A3" 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>
|
||||
Unlock content with <a class="inline-block ml-2 text-blue-400 hover:sm:text-white" href="/pricing">Pro Subscription</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</main>
|
||||
</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>
|
||||
@ -77,6 +77,10 @@ export const failToDeliverComponent= writable(<boolean>(false));
|
||||
export const borrowedShareComponent= writable(<boolean>(false));
|
||||
export const impliedVolatilityComponent= writable(<boolean>(false));
|
||||
export const optionsNetFlowComponent= writable(<boolean>(false));
|
||||
export const governmentContractComponent= writable(<boolean>(false));
|
||||
|
||||
|
||||
|
||||
|
||||
export const strategyId = writable(<string> (""));
|
||||
export const articleId = writable(<string> (""));
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import {AreaSeries, Chart, PriceLine, CandlestickSeries} from 'svelte-lightweight-charts';
|
||||
|
||||
import { TrackingModeExitMode } from 'lightweight-charts';
|
||||
import {getCache, setCache, optionsNetFlowComponent, impliedVolatilityComponent, borrowedShareComponent, clinicalTrialComponent, optionComponent, failToDeliverComponent, marketMakerComponent, analystEstimateComponent, sentimentComponent, screenWidth, displayCompanyName, numberOfUnreadNotification, globalForm, varComponent, shareStatisticsComponent, enterpriseComponent, darkPoolComponent, retailVolumeComponent, shareholderComponent, trendAnalysisComponent, revenueSegmentationComponent, priceAnalysisComponent, fundamentalAnalysisComponent, userRegion, isCrosshairMoveActive, realtimePrice, priceIncrease, currentPortfolioPrice, currentPrice, stockTicker, isOpen, isBeforeMarketOpen, isWeekend} from '$lib/store';
|
||||
import {getCache, setCache, governmentContractComponent, optionsNetFlowComponent, impliedVolatilityComponent, borrowedShareComponent, clinicalTrialComponent, optionComponent, failToDeliverComponent, marketMakerComponent, analystEstimateComponent, sentimentComponent, screenWidth, displayCompanyName, numberOfUnreadNotification, globalForm, varComponent, shareStatisticsComponent, enterpriseComponent, darkPoolComponent, retailVolumeComponent, shareholderComponent, trendAnalysisComponent, revenueSegmentationComponent, priceAnalysisComponent, fundamentalAnalysisComponent, userRegion, isCrosshairMoveActive, realtimePrice, priceIncrease, currentPortfolioPrice, currentPrice, stockTicker, isOpen, isBeforeMarketOpen, isWeekend} from '$lib/store';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import BullBearSay from '$lib/components/BullBearSay.svelte';
|
||||
import CommunitySentiment from '$lib/components/CommunitySentiment.svelte';
|
||||
@ -1304,7 +1304,16 @@ function changeChartType() {
|
||||
</div>
|
||||
</Lazy>
|
||||
|
||||
|
||||
|
||||
<Lazy>
|
||||
<div class="w-full mt-10 sm:mt-5 m-auto sm:pl-6 sm:pb-6 sm:pt-6 {!$governmentContractComponent ? 'hidden' : ''}">
|
||||
{#await import('$lib/components/GovernmentContract.svelte') then {default: Comp}}
|
||||
<svelte:component this={Comp} data={data} />
|
||||
{/await}
|
||||
</div>
|
||||
</Lazy>
|
||||
|
||||
|
||||
<Lazy>
|
||||
<div class="w-full mt-10 sm:mt-5 m-auto sm:pl-6 sm:pb-6 sm:pt-6 {!$enterpriseComponent ? 'hidden' : ''}">
|
||||
{#await import('$lib/components/Enterprise.svelte') then {default: Comp}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user