update financial page
This commit is contained in:
parent
32d0791690
commit
931792a47e
@ -72,6 +72,10 @@
|
|||||||
Object.keys(sectionMap).find(
|
Object.keys(sectionMap).find(
|
||||||
(key) => sectionMap[key] === foundSection,
|
(key) => sectionMap[key] === foundSection,
|
||||||
) || "income";
|
) || "income";
|
||||||
|
|
||||||
|
if (displaySubSection === "ratios" && $selectedTimePeriod === "ttm") {
|
||||||
|
$selectedTimePeriod = "annual";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -156,16 +160,18 @@
|
|||||||
>
|
>
|
||||||
Quarterly
|
Quarterly
|
||||||
</a>
|
</a>
|
||||||
<a
|
{#if displaySubSection !== "ratios"}
|
||||||
href={$page?.url?.pathname + "?query=ttm"}
|
<a
|
||||||
on:click|preventDefault={() => updateQuery("ttm")}
|
href={$page?.url?.pathname + "?query=ttm"}
|
||||||
class="hidden sm:block p-2 px-5 cursor-pointer {$selectedTimePeriod ===
|
on:click|preventDefault={() => updateQuery("ttm")}
|
||||||
'ttm'
|
class="hidden sm:block p-2 px-5 cursor-pointer {$selectedTimePeriod ===
|
||||||
? 'text-muted dark:text-white bg-[#EEEEEE] dark:bg-primary/90 font-semibold'
|
'ttm'
|
||||||
: 'text-blue-700 dark:text-gray-400 sm:hover:text-muted dark:sm:hover:text-white sm:hover:bg-[#EEEEEE] dark:sm:hover:bg-primary/90'}"
|
? 'text-muted dark:text-white bg-[#EEEEEE] dark:bg-primary/90 font-semibold'
|
||||||
>
|
: 'text-blue-700 dark:text-gray-400 sm:hover:text-muted dark:sm:hover:text-white sm:hover:bg-[#EEEEEE] dark:sm:hover:bg-primary/90'}"
|
||||||
TTM
|
>
|
||||||
</a>
|
TTM
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="flex flex-row items-center w-full mt-1 sm:hidden">
|
<ul class="flex flex-row items-center w-full mt-1 sm:hidden">
|
||||||
@ -189,15 +195,18 @@
|
|||||||
>
|
>
|
||||||
Quarterly
|
Quarterly
|
||||||
</a>
|
</a>
|
||||||
<a
|
{#if displaySubSection !== "ratios"}
|
||||||
href={$page?.url?.pathname + "?query=ttm"}
|
<a
|
||||||
on:click|preventDefault={() => updateQuery("ttm")}
|
href={$page?.url?.pathname + "?query=ttm"}
|
||||||
class="p-2 px-5 cursor-pointer {$selectedTimePeriod === 'ttm'
|
on:click|preventDefault={() => updateQuery("ttm")}
|
||||||
? 'text-muted dark:text-white bg-[#EEEEEE] dark:bg-primary/90 font-semibold'
|
class="p-2 px-5 cursor-pointer {$selectedTimePeriod ===
|
||||||
: 'text-blue-700 dark:text-gray-400 sm:hover:text-muted dark:sm:hover:text-white sm:hover:bg-[#EEEEEE] dark:sm:hover:bg-primary/90'}"
|
'ttm'
|
||||||
>
|
? 'text-muted dark:text-white bg-[#EEEEEE] dark:bg-primary/90 font-semibold'
|
||||||
TTM
|
: 'text-blue-700 dark:text-gray-400 sm:hover:text-muted dark:sm:hover:text-white sm:hover:bg-[#EEEEEE] dark:sm:hover:bg-primary/90'}"
|
||||||
</a>
|
>
|
||||||
|
TTM
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -452,7 +452,9 @@
|
|||||||
{"FY" + " " + item?.fiscalYear}
|
{"FY" + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td class=" font-semibold text-sm text-end">
|
<td
|
||||||
|
class="min-w-[130px] font-semibold text-sm text-end"
|
||||||
|
>
|
||||||
{item?.period + " " + item?.fiscalYear}
|
{item?.period + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
import { getCache, setCache } from "$lib/store";
|
|
||||||
|
|
||||||
export const load = async ({ locals, params }) => {
|
export const load = async ({ locals, params }) => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const { apiKey, apiURL } = locals;
|
const { apiKey, apiURL } = locals;
|
||||||
const postData = {
|
const postData = {
|
||||||
ticker: params.tickerID,
|
ticker: params.tickerID,
|
||||||
|
statement: 'balance-sheet-statement',
|
||||||
};
|
};
|
||||||
|
|
||||||
// make the POST request to the endpoint
|
// make the POST request to the endpoint
|
||||||
const response = await fetch(apiURL + "/stock-balance-sheet", {
|
const response = await fetch(apiURL + "/financial-statement", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -22,6 +20,8 @@ export const load = async ({ locals, params }) => {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
// Make sure to return a promise
|
||||||
return {
|
return {
|
||||||
getData: await getData(),
|
getData: await getData(),
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
stockTicker,
|
stockTicker,
|
||||||
coolMode,
|
coolMode,
|
||||||
timeFrame,
|
timeFrame,
|
||||||
|
selectedTimePeriod,
|
||||||
} from "$lib/store";
|
} from "$lib/store";
|
||||||
import { removeCompanyStrings } from "$lib/utils";
|
import { removeCompanyStrings } from "$lib/utils";
|
||||||
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
||||||
@ -16,25 +17,11 @@
|
|||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
let isLoaded = true;
|
|
||||||
let tableList = [];
|
let tableList = [];
|
||||||
let processedData = {};
|
let processedData = {};
|
||||||
|
|
||||||
let financialData = [];
|
let financialData = [];
|
||||||
let fullStatement = [];
|
let fullStatement = [];
|
||||||
let filterRule = "annual";
|
|
||||||
let displayStatement = "revenue";
|
|
||||||
|
|
||||||
let activeIdx = 0;
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
title: "Annual",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Quarterly",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const statementConfig = [
|
const statementConfig = [
|
||||||
{
|
{
|
||||||
@ -229,7 +216,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
fullStatement = data?.getData;
|
fullStatement = data?.getData;
|
||||||
displayStatement = "revenue";
|
|
||||||
|
|
||||||
const exportFundamentalData = (format = "csv") => {
|
const exportFundamentalData = (format = "csv") => {
|
||||||
if (["Pro", "Plus"]?.includes(data?.user?.tier)) {
|
if (["Pro", "Plus"]?.includes(data?.user?.tier)) {
|
||||||
@ -240,8 +226,8 @@
|
|||||||
|
|
||||||
let properties = [
|
let properties = [
|
||||||
{
|
{
|
||||||
key: filterRule === "annual" ? "fiscalYear" : "date",
|
key: $selectedTimePeriod === "annual" ? "fiscalYear" : "date",
|
||||||
label: filterRule === "annual" ? "Year" : "Quarter",
|
label: $selectedTimePeriod === "annual" ? "Year" : "Quarter",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -274,7 +260,7 @@
|
|||||||
a.href = url;
|
a.href = url;
|
||||||
a.download =
|
a.download =
|
||||||
$stockTicker.toLowerCase() +
|
$stockTicker.toLowerCase() +
|
||||||
`${filterRule === "annual" ? "_annual" : "_quarter"}_balance_sheet_statement.csv`;
|
`${$selectedTimePeriod === "annual" ? "_annual" : $selectedTimePeriod === "quarterly" ? "_quarter" : "_ttm"}_balance_sheet_statement.csv`;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
@ -297,13 +283,16 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Precompute xList from income (reverse order)
|
||||||
const xList = [];
|
const xList = [];
|
||||||
for (let i = financialData.length - 1; i >= 0; i--) {
|
for (let i = financialData.length - 1; i >= 0; i--) {
|
||||||
const statement = financialData[i];
|
const statement = financialData[i];
|
||||||
const year = statement.fiscalYear.slice(-2);
|
const year = statement.fiscalYear.slice(-2);
|
||||||
const quarter = statement.period;
|
const quarter = statement.period;
|
||||||
xList.push(
|
xList.push(
|
||||||
filterRule === "annual" ? "FY" + year : "FY" + year + " " + quarter,
|
$selectedTimePeriod === "annual"
|
||||||
|
? "FY" + year
|
||||||
|
: "FY" + year + " " + quarter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +320,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Build tableList once for all charts and sort by date (newest first)
|
// Build tableList once for all charts and sort by date (newest first)
|
||||||
tableList = financialData.map((statement) => ({
|
tableList = financialData?.map((statement) => ({
|
||||||
date: statement.date,
|
date: statement.date,
|
||||||
// Add more properties if needed
|
// Add more properties if needed
|
||||||
}));
|
}));
|
||||||
@ -340,14 +329,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if ($timeFrame || activeIdx) {
|
if ($timeFrame || $selectedTimePeriod) {
|
||||||
if (activeIdx === 0) {
|
if ($selectedTimePeriod === "annual") {
|
||||||
filterRule = "annual";
|
|
||||||
fullStatement = data?.getData?.annual;
|
fullStatement = data?.getData?.annual;
|
||||||
} else {
|
} else if ($selectedTimePeriod === "quarterly") {
|
||||||
filterRule = "quarterly";
|
|
||||||
fullStatement = data?.getData?.quarter;
|
fullStatement = data?.getData?.quarter;
|
||||||
|
} else if ($selectedTimePeriod === "ttm") {
|
||||||
|
fullStatement = data?.getData?.ttm;
|
||||||
|
} else {
|
||||||
|
fullStatement = data?.getData?.annual;
|
||||||
}
|
}
|
||||||
|
|
||||||
financialData = filterStatement(fullStatement, $timeFrame);
|
financialData = filterStatement(fullStatement, $timeFrame);
|
||||||
preprocessFinancialData();
|
preprocessFinancialData();
|
||||||
}
|
}
|
||||||
@ -355,8 +347,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SEO
|
<SEO
|
||||||
title={`${$displayCompanyName} (${$stockTicker}) Balance Sheet`}
|
title={`${$displayCompanyName} (${$stockTicker}) Cash Flow Statement`}
|
||||||
description={`Detailed balance sheet for ${$displayCompanyName} (${$stockTicker}), including cash, debt, assets, liabilities, and book value.`}
|
description={`Detailed cash flow statements for ${$displayCompanyName} (${$stockTicker}), including operating cash flow, capex and free cash flow.`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section class=" w-full overflow-hidden h-full">
|
<section class=" w-full overflow-hidden h-full">
|
||||||
@ -367,113 +359,70 @@
|
|||||||
<main class="w-full">
|
<main class="w-full">
|
||||||
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
|
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
|
||||||
<div
|
<div
|
||||||
class="mb-3 flex flex-col sm:flex-row items-start sm:items-center justify-between"
|
class="mb-3 sm:mb-0 flex flex-col sm:flex-row items-start sm:items-center justify-between"
|
||||||
>
|
>
|
||||||
<h1 class="text-xl sm:text-2xl font-bold">
|
<h1 class="text-xl sm:text-2xl font-bold">
|
||||||
{removeCompanyStrings($displayCompanyName)} Balance Sheet Statement
|
{removeCompanyStrings($displayCompanyName)} Income Statement
|
||||||
</h1>
|
</h1>
|
||||||
<div
|
<label class="inline-flex sm:hidden mt-4 cursor-pointer relative">
|
||||||
class="mt-3 sm:mt-0 mb-2 sm:mb-0 bg-gray-300 dark:bg-secondary w-full min-w-24 sm:w-fit relative flex flex-wrap items-center justify-center rounded-md p-1"
|
<input
|
||||||
>
|
on:click={toggleMode}
|
||||||
{#each tabs as item, i}
|
type="checkbox"
|
||||||
{#if !["Pro", "Plus"]?.includes(data?.user?.tier) && i > 0}
|
checked={$coolMode}
|
||||||
<button
|
value={$coolMode}
|
||||||
on:click={() => goto("/pricing")}
|
class="sr-only peer"
|
||||||
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
/>
|
||||||
>
|
<div
|
||||||
<span class="relative text-sm block font-semibold">
|
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
||||||
{item.title}
|
></div>
|
||||||
<svg
|
{#if $coolMode}
|
||||||
class="inline-block ml-0.5 -mt-1 w-3.5 h-3.5"
|
<span class="ml-2 text-sm"> Table Mode </span>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
{:else}
|
||||||
viewBox="0 0 24 24"
|
<span class="ml-2 text-sm"> Chart Mode </span>
|
||||||
><path
|
{/if}
|
||||||
fill="currentColor"
|
</label>
|
||||||
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
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
on:click={() => (activeIdx = i)}
|
|
||||||
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
|
||||||
i
|
|
||||||
? 'z-0'
|
|
||||||
: ''} "
|
|
||||||
>
|
|
||||||
{#if activeIdx === i}
|
|
||||||
<div class="absolute inset-0 rounded-md bg-[#fff]"></div>
|
|
||||||
{/if}
|
|
||||||
<span
|
|
||||||
class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
|
|
||||||
i
|
|
||||||
? 'text-black'
|
|
||||||
: ''}"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-2">
|
<div class="grid grid-cols-1 gap-2">
|
||||||
{#if financialData?.length > 0}
|
{#if financialData?.length > 0}
|
||||||
<div
|
<div
|
||||||
class="flex flex-row items-center w-full justify-end sm:justify-center"
|
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
|
||||||
>
|
>
|
||||||
<label
|
<span
|
||||||
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
|
class="text-xs sm:text-sm order-1 sm:order-0 mt-5 sm:mt-0 text-gray-600 dark:text-gray-400 w-full"
|
||||||
>
|
>
|
||||||
<input
|
Financials in {financialData?.at(0)?.reportedCurrency}. Fiscal
|
||||||
on:click={toggleMode}
|
year is
|
||||||
type="checkbox"
|
{data?.getProfileData?.fiscalYearRange}.
|
||||||
checked={$coolMode}
|
</span>
|
||||||
value={$coolMode}
|
<div class="flex flex-row items-center justify-end w-full">
|
||||||
class="sr-only peer"
|
<label
|
||||||
/>
|
class="hidden sm:inline-flex ml-auto mt-2 sm:mt-0 cursor-pointer relative"
|
||||||
<div
|
>
|
||||||
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
<input
|
||||||
></div>
|
on:click={toggleMode}
|
||||||
{#if $coolMode}
|
type="checkbox"
|
||||||
<span class="ml-2 text-sm"> Table Mode </span>
|
checked={$coolMode}
|
||||||
{:else}
|
value={$coolMode}
|
||||||
<span class="ml-2 text-sm"> Chart Mode </span>
|
class="sr-only peer"
|
||||||
{/if}
|
/>
|
||||||
</label>
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
||||||
<div class="flex flex-row items-center w-fit sm:ml-auto">
|
></div>
|
||||||
<!--
|
{#if $coolMode}
|
||||||
<div class="ml-2 relative inline-block text-left">
|
<span class="ml-2 text-sm"> Table Mode </span>
|
||||||
<Button
|
{:else}
|
||||||
class="shadow-sm w-full border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-5 rounded-md truncate"
|
<span class="ml-2 text-sm"> Chart Mode </span>
|
||||||
>
|
{/if}
|
||||||
<svg
|
</label>
|
||||||
class="w-4 h-4 bp:w-5 bp:h-5 pointer-events-none"
|
<div class="ml-auto sm:ml-5 relative inline-block">
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
style="max-width:40px"
|
|
||||||
><path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"
|
|
||||||
></path></svg
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ml-2 relative inline-block text-left grow">
|
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
<DropdownMenu.Trigger asChild let:builder>
|
||||||
<Button
|
<Button
|
||||||
builders={[builder]}
|
builders={[builder]}
|
||||||
class="shadow-sm w-full border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
class="shadow-sm w-full sm:w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
||||||
>
|
>
|
||||||
<span class="truncate">Raw</span>
|
<span class="truncate">{$timeFrame}</span>
|
||||||
<svg
|
<svg
|
||||||
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -492,33 +441,55 @@
|
|||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
||||||
>
|
>
|
||||||
|
<DropdownMenu.Label
|
||||||
|
class="text-muted dark:text-gray-400"
|
||||||
|
>
|
||||||
|
Select time frame
|
||||||
|
</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "5Y")}
|
on:click={() => ($timeFrame = "5Y")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Billions
|
5 years
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "10Y")}
|
on:click={() => ($timeFrame = "10Y")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Millions
|
10 years
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "MAX")}
|
on:click={() => ($timeFrame = "MAX")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Raw
|
Max
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
|
<Button
|
||||||
|
on:click={() => exportFundamentalData("csv")}
|
||||||
|
class="shadow-sm ml-2 w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
||||||
|
>
|
||||||
|
<span class="truncate">Download</span>
|
||||||
|
<svg
|
||||||
|
class="{['Pro', 'Plus']?.includes(data?.user?.tier)
|
||||||
|
? 'hidden'
|
||||||
|
: ''} ml-1 -mt-0.5 w-3.5 h-3.5"
|
||||||
|
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
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $coolMode}
|
{#if $coolMode}
|
||||||
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
|
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
|
||||||
{#each fields as item, i}
|
{#each fields as item, i}
|
||||||
@ -526,7 +497,7 @@
|
|||||||
data={financialData}
|
data={financialData}
|
||||||
{statementConfig}
|
{statementConfig}
|
||||||
displayStatement={item?.key}
|
displayStatement={item?.key}
|
||||||
{filterRule}
|
filterRule={$selectedTimePeriod}
|
||||||
{processedData}
|
{processedData}
|
||||||
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
|
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
|
||||||
i % 4
|
i % 4
|
||||||
@ -535,91 +506,6 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
|
||||||
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="text-xs sm:text-sm order-1 sm:order-0 mt-5 sm:mt-0 text-gray-600 dark:text-gray-400 w-full"
|
|
||||||
>
|
|
||||||
Financials in {data?.getProfileData?.currency}. Fiscal year
|
|
||||||
is
|
|
||||||
{data?.getProfileData?.fiscalYearRange}.
|
|
||||||
</span>
|
|
||||||
<div class="flex flex-row items-center justify-end w-full">
|
|
||||||
<div class="sm:ml-auto relative inline-block">
|
|
||||||
<DropdownMenu.Root>
|
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
|
||||||
<Button
|
|
||||||
builders={[builder]}
|
|
||||||
class="shadow-sm w-full sm:w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
|
||||||
>
|
|
||||||
<span class="truncate">{$timeFrame}</span>
|
|
||||||
<svg
|
|
||||||
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
style="max-width:40px"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
<DropdownMenu.Content
|
|
||||||
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
|
||||||
>
|
|
||||||
<DropdownMenu.Label
|
|
||||||
class="text-muted dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Select time frame
|
|
||||||
</DropdownMenu.Label>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "5Y")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
5 years
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "10Y")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
10 years
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "MAX")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
Max
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
on:click={() => exportFundamentalData("csv")}
|
|
||||||
class="shadow-sm ml-2 w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
|
||||||
>
|
|
||||||
<span class="truncate">Download</span>
|
|
||||||
<svg
|
|
||||||
class="{['Pro', 'Plus']?.includes(data?.user?.tier)
|
|
||||||
? 'hidden'
|
|
||||||
: ''} ml-1 -mt-0.5 w-3.5 h-3.5"
|
|
||||||
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
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
|
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
|
||||||
>
|
>
|
||||||
@ -632,14 +518,16 @@
|
|||||||
>Fiscal Year</td
|
>Fiscal Year</td
|
||||||
>
|
>
|
||||||
{#each financialData as item}
|
{#each financialData as item}
|
||||||
{#if filterRule === "annual"}
|
{#if $selectedTimePeriod === "annual"}
|
||||||
<td
|
<td
|
||||||
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
|
class="min-w-[130px] font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
|
||||||
>
|
>
|
||||||
{"FY" + " " + item?.fiscalYear}
|
{"FY" + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td class=" font-semibold text-sm text-end">
|
<td
|
||||||
|
class="min-w-[130px] font-semibold text-sm text-end"
|
||||||
|
>
|
||||||
{item?.period + " " + item?.fiscalYear}
|
{item?.period + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
export const load = async ({ locals, params }) => {
|
export const load = async ({ locals, params }) => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const { apiKey, apiURL } = locals;
|
const { apiKey, apiURL } = locals;
|
||||||
|
|
||||||
const postData = {
|
const postData = {
|
||||||
ticker: params.tickerID,
|
ticker: params.tickerID,
|
||||||
|
statement: 'cash-flow-statement',
|
||||||
};
|
};
|
||||||
|
|
||||||
// make the POST request to the endpoint
|
// make the POST request to the endpoint
|
||||||
const response = await fetch(apiURL + "/stock-cash-flow", {
|
const response = await fetch(apiURL + "/financial-statement", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -21,6 +20,8 @@ export const load = async ({ locals, params }) => {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
// Make sure to return a promise
|
||||||
return {
|
return {
|
||||||
getData: await getData(),
|
getData: await getData(),
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
stockTicker,
|
stockTicker,
|
||||||
coolMode,
|
coolMode,
|
||||||
timeFrame,
|
timeFrame,
|
||||||
|
selectedTimePeriod,
|
||||||
} from "$lib/store";
|
} from "$lib/store";
|
||||||
import { removeCompanyStrings } from "$lib/utils";
|
import { removeCompanyStrings } from "$lib/utils";
|
||||||
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
||||||
@ -16,24 +17,11 @@
|
|||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
let isLoaded = true;
|
|
||||||
let tableList = [];
|
let tableList = [];
|
||||||
let processedData = {};
|
let processedData = {};
|
||||||
|
|
||||||
let financialData = [];
|
let financialData = [];
|
||||||
let fullStatement = [];
|
let fullStatement = [];
|
||||||
let filterRule = "annual";
|
|
||||||
|
|
||||||
let activeIdx = 0;
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
title: "Annual",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Quarterly",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const statementConfig = [
|
const statementConfig = [
|
||||||
{
|
{
|
||||||
@ -163,7 +151,6 @@
|
|||||||
text: "Free cash flow is the cash remaining after the company spends on everything required to maintain and grow the business. It is calculated by subtracting capital expenditures from operating cash flow.",
|
text: "Free cash flow is the cash remaining after the company spends on everything required to maintain and grow the business. It is calculated by subtracting capital expenditures from operating cash flow.",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const fields = statementConfig.map((item) => ({
|
const fields = statementConfig.map((item) => ({
|
||||||
label: item.label,
|
label: item.label,
|
||||||
key: item.propertyName,
|
key: item.propertyName,
|
||||||
@ -202,8 +189,8 @@
|
|||||||
|
|
||||||
let properties = [
|
let properties = [
|
||||||
{
|
{
|
||||||
key: filterRule === "annual" ? "fiscalYear" : "date",
|
key: $selectedTimePeriod === "annual" ? "fiscalYear" : "date",
|
||||||
label: filterRule === "annual" ? "Year" : "Quarter",
|
label: $selectedTimePeriod === "annual" ? "Year" : "Quarter",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -236,7 +223,7 @@
|
|||||||
a.href = url;
|
a.href = url;
|
||||||
a.download =
|
a.download =
|
||||||
$stockTicker.toLowerCase() +
|
$stockTicker.toLowerCase() +
|
||||||
`${filterRule === "annual" ? "_annual" : "_quarter"}_cash_flow_statement.csv`;
|
`${$selectedTimePeriod === "annual" ? "_annual" : $selectedTimePeriod === "quarterly" ? "_quarter" : "_ttm"}_cash_flow_statement.csv`;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
@ -259,13 +246,16 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Precompute xList from income (reverse order)
|
||||||
const xList = [];
|
const xList = [];
|
||||||
for (let i = financialData.length - 1; i >= 0; i--) {
|
for (let i = financialData.length - 1; i >= 0; i--) {
|
||||||
const statement = financialData[i];
|
const statement = financialData[i];
|
||||||
const year = statement.fiscalYear.slice(-2);
|
const year = statement.fiscalYear.slice(-2);
|
||||||
const quarter = statement.period;
|
const quarter = statement.period;
|
||||||
xList.push(
|
xList.push(
|
||||||
filterRule === "annual" ? "FY" + year : "FY" + year + " " + quarter,
|
$selectedTimePeriod === "annual"
|
||||||
|
? "FY" + year
|
||||||
|
: "FY" + year + " " + quarter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +283,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Build tableList once for all charts and sort by date (newest first)
|
// Build tableList once for all charts and sort by date (newest first)
|
||||||
tableList = financialData.map((statement) => ({
|
tableList = financialData?.map((statement) => ({
|
||||||
date: statement.date,
|
date: statement.date,
|
||||||
// Add more properties if needed
|
// Add more properties if needed
|
||||||
}));
|
}));
|
||||||
@ -302,14 +292,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if ($timeFrame || activeIdx) {
|
if ($timeFrame || $selectedTimePeriod) {
|
||||||
if (activeIdx === 0) {
|
if ($selectedTimePeriod === "annual") {
|
||||||
filterRule = "annual";
|
|
||||||
fullStatement = data?.getData?.annual;
|
fullStatement = data?.getData?.annual;
|
||||||
} else {
|
} else if ($selectedTimePeriod === "quarterly") {
|
||||||
filterRule = "quarterly";
|
|
||||||
fullStatement = data?.getData?.quarter;
|
fullStatement = data?.getData?.quarter;
|
||||||
|
} else if ($selectedTimePeriod === "ttm") {
|
||||||
|
fullStatement = data?.getData?.ttm;
|
||||||
|
} else {
|
||||||
|
fullStatement = data?.getData?.annual;
|
||||||
}
|
}
|
||||||
|
|
||||||
financialData = filterStatement(fullStatement, $timeFrame);
|
financialData = filterStatement(fullStatement, $timeFrame);
|
||||||
preprocessFinancialData();
|
preprocessFinancialData();
|
||||||
}
|
}
|
||||||
@ -329,113 +322,70 @@
|
|||||||
<main class="w-full">
|
<main class="w-full">
|
||||||
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
|
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
|
||||||
<div
|
<div
|
||||||
class="mb-3 flex flex-col sm:flex-row items-start sm:items-center justify-between"
|
class="mb-3 sm:mb-0 flex flex-col sm:flex-row items-start sm:items-center justify-between"
|
||||||
>
|
>
|
||||||
<h1 class="text-xl sm:text-2xl font-bold">
|
<h1 class="text-xl sm:text-2xl font-bold">
|
||||||
{removeCompanyStrings($displayCompanyName)} Cash Flow Statement
|
{removeCompanyStrings($displayCompanyName)} Income Statement
|
||||||
</h1>
|
</h1>
|
||||||
<div
|
<label class="inline-flex sm:hidden mt-4 cursor-pointer relative">
|
||||||
class="mt-3 sm:mt-0 mb-2 sm:mb-0 bg-gray-300 dark:bg-secondary w-full min-w-24 sm:w-fit relative flex flex-wrap items-center justify-center rounded-md p-1"
|
<input
|
||||||
>
|
on:click={toggleMode}
|
||||||
{#each tabs as item, i}
|
type="checkbox"
|
||||||
{#if !["Pro", "Plus"]?.includes(data?.user?.tier) && i > 0}
|
checked={$coolMode}
|
||||||
<button
|
value={$coolMode}
|
||||||
on:click={() => goto("/pricing")}
|
class="sr-only peer"
|
||||||
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
/>
|
||||||
>
|
<div
|
||||||
<span class="relative text-sm block font-semibold">
|
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
||||||
{item.title}
|
></div>
|
||||||
<svg
|
{#if $coolMode}
|
||||||
class="inline-block ml-0.5 -mt-1 w-3.5 h-3.5"
|
<span class="ml-2 text-sm"> Table Mode </span>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
{:else}
|
||||||
viewBox="0 0 24 24"
|
<span class="ml-2 text-sm"> Chart Mode </span>
|
||||||
><path
|
{/if}
|
||||||
fill="currentColor"
|
</label>
|
||||||
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
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
on:click={() => (activeIdx = i)}
|
|
||||||
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
|
||||||
i
|
|
||||||
? 'z-0'
|
|
||||||
: ''} "
|
|
||||||
>
|
|
||||||
{#if activeIdx === i}
|
|
||||||
<div class="absolute inset-0 rounded-md bg-[#fff]"></div>
|
|
||||||
{/if}
|
|
||||||
<span
|
|
||||||
class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
|
|
||||||
i
|
|
||||||
? 'text-black'
|
|
||||||
: ''}"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-2">
|
<div class="grid grid-cols-1 gap-2">
|
||||||
{#if financialData?.length > 0}
|
{#if financialData?.length > 0}
|
||||||
<div
|
<div
|
||||||
class="flex flex-row items-center w-full justify-end sm:justify-center"
|
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
|
||||||
>
|
>
|
||||||
<label
|
<span
|
||||||
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
|
class="text-xs sm:text-sm order-1 sm:order-0 mt-5 sm:mt-0 text-gray-600 dark:text-gray-400 w-full"
|
||||||
>
|
>
|
||||||
<input
|
Financials in {financialData?.at(0)?.reportedCurrency}. Fiscal
|
||||||
on:click={toggleMode}
|
year is
|
||||||
type="checkbox"
|
{data?.getProfileData?.fiscalYearRange}.
|
||||||
checked={$coolMode}
|
</span>
|
||||||
value={$coolMode}
|
<div class="flex flex-row items-center justify-end w-full">
|
||||||
class="sr-only peer"
|
<label
|
||||||
/>
|
class="hidden sm:inline-flex ml-auto mt-2 sm:mt-0 cursor-pointer relative"
|
||||||
<div
|
>
|
||||||
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
<input
|
||||||
></div>
|
on:click={toggleMode}
|
||||||
{#if $coolMode}
|
type="checkbox"
|
||||||
<span class="ml-2 text-sm"> Table Mode </span>
|
checked={$coolMode}
|
||||||
{:else}
|
value={$coolMode}
|
||||||
<span class="ml-2 text-sm"> Chart Mode </span>
|
class="sr-only peer"
|
||||||
{/if}
|
/>
|
||||||
</label>
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
||||||
<div class="flex flex-row items-center w-fit sm:ml-auto">
|
></div>
|
||||||
<!--
|
{#if $coolMode}
|
||||||
<div class="ml-2 relative inline-block text-left">
|
<span class="ml-2 text-sm"> Table Mode </span>
|
||||||
<Button
|
{:else}
|
||||||
class="shadow-sm w-full border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-5 rounded-md truncate"
|
<span class="ml-2 text-sm"> Chart Mode </span>
|
||||||
>
|
{/if}
|
||||||
<svg
|
</label>
|
||||||
class="w-4 h-4 bp:w-5 bp:h-5 pointer-events-none"
|
<div class="ml-auto sm:ml-5 relative inline-block">
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
style="max-width:40px"
|
|
||||||
><path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"
|
|
||||||
></path></svg
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ml-2 relative inline-block text-left grow">
|
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
<DropdownMenu.Trigger asChild let:builder>
|
||||||
<Button
|
<Button
|
||||||
builders={[builder]}
|
builders={[builder]}
|
||||||
class="shadow-sm w-full border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
class="shadow-sm w-full sm:w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
||||||
>
|
>
|
||||||
<span class="truncate">Raw</span>
|
<span class="truncate">{$timeFrame}</span>
|
||||||
<svg
|
<svg
|
||||||
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -454,33 +404,55 @@
|
|||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
||||||
>
|
>
|
||||||
|
<DropdownMenu.Label
|
||||||
|
class="text-muted dark:text-gray-400"
|
||||||
|
>
|
||||||
|
Select time frame
|
||||||
|
</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "5Y")}
|
on:click={() => ($timeFrame = "5Y")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Billions
|
5 years
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "10Y")}
|
on:click={() => ($timeFrame = "10Y")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Millions
|
10 years
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "MAX")}
|
on:click={() => ($timeFrame = "MAX")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Raw
|
Max
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
|
<Button
|
||||||
|
on:click={() => exportFundamentalData("csv")}
|
||||||
|
class="shadow-sm ml-2 w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
||||||
|
>
|
||||||
|
<span class="truncate">Download</span>
|
||||||
|
<svg
|
||||||
|
class="{['Pro', 'Plus']?.includes(data?.user?.tier)
|
||||||
|
? 'hidden'
|
||||||
|
: ''} ml-1 -mt-0.5 w-3.5 h-3.5"
|
||||||
|
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
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $coolMode}
|
{#if $coolMode}
|
||||||
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
|
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
|
||||||
{#each fields as item, i}
|
{#each fields as item, i}
|
||||||
@ -488,7 +460,7 @@
|
|||||||
data={financialData}
|
data={financialData}
|
||||||
{statementConfig}
|
{statementConfig}
|
||||||
displayStatement={item?.key}
|
displayStatement={item?.key}
|
||||||
{filterRule}
|
filterRule={$selectedTimePeriod}
|
||||||
{processedData}
|
{processedData}
|
||||||
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
|
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
|
||||||
i % 4
|
i % 4
|
||||||
@ -497,91 +469,6 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
|
||||||
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="text-xs sm:text-sm order-1 sm:order-0 mt-5 sm:mt-0 text-gray-600 dark:text-gray-400 w-full"
|
|
||||||
>
|
|
||||||
Financials in {data?.getProfileData?.currency}. Fiscal year
|
|
||||||
is
|
|
||||||
{data?.getProfileData?.fiscalYearRange}.
|
|
||||||
</span>
|
|
||||||
<div class="flex flex-row items-center justify-end w-full">
|
|
||||||
<div class="sm:ml-auto relative inline-block">
|
|
||||||
<DropdownMenu.Root>
|
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
|
||||||
<Button
|
|
||||||
builders={[builder]}
|
|
||||||
class="shadow-sm w-full sm:w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
|
||||||
>
|
|
||||||
<span class="truncate">{$timeFrame}</span>
|
|
||||||
<svg
|
|
||||||
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
style="max-width:40px"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
<DropdownMenu.Content
|
|
||||||
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
|
||||||
>
|
|
||||||
<DropdownMenu.Label
|
|
||||||
class="text-muted dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Select time frame
|
|
||||||
</DropdownMenu.Label>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "5Y")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
5 years
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "10Y")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
10 years
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "MAX")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
Max
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
on:click={() => exportFundamentalData("csv")}
|
|
||||||
class="shadow-sm ml-2 w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
|
||||||
>
|
|
||||||
<span class="truncate">Download</span>
|
|
||||||
<svg
|
|
||||||
class="{['Pro', 'Plus']?.includes(data?.user?.tier)
|
|
||||||
? 'hidden'
|
|
||||||
: ''} ml-1 -mt-0.5 w-3.5 h-3.5"
|
|
||||||
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
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
|
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
|
||||||
>
|
>
|
||||||
@ -594,14 +481,16 @@
|
|||||||
>Fiscal Year</td
|
>Fiscal Year</td
|
||||||
>
|
>
|
||||||
{#each financialData as item}
|
{#each financialData as item}
|
||||||
{#if filterRule === "annual"}
|
{#if $selectedTimePeriod === "annual"}
|
||||||
<td
|
<td
|
||||||
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
|
class="min-w-[130px] font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
|
||||||
>
|
>
|
||||||
{"FY" + " " + item?.fiscalYear}
|
{"FY" + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td class=" font-semibold text-sm text-end">
|
<td
|
||||||
|
class="min-w-[130px] font-semibold text-sm text-end"
|
||||||
|
>
|
||||||
{item?.period + " " + item?.fiscalYear}
|
{item?.period + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
export const load = async ({ locals, params }) => {
|
export const load = async ({ locals, params }) => {
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const { apiKey, apiURL } = locals;
|
const { apiKey, apiURL } = locals;
|
||||||
|
|
||||||
const postData = {
|
const postData = {
|
||||||
ticker: params.tickerID,
|
ticker: params.tickerID,
|
||||||
|
statement: 'ratios',
|
||||||
};
|
};
|
||||||
|
|
||||||
// make the POST request to the endpoint
|
// make the POST request to the endpoint
|
||||||
const response = await fetch(apiURL + "/stock-ratios", {
|
const response = await fetch(apiURL + "/financial-statement", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -21,6 +20,8 @@ export const load = async ({ locals, params }) => {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
// Make sure to return a promise
|
||||||
return {
|
return {
|
||||||
getData: await getData(),
|
getData: await getData(),
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
stockTicker,
|
stockTicker,
|
||||||
coolMode,
|
coolMode,
|
||||||
timeFrame,
|
timeFrame,
|
||||||
|
selectedTimePeriod,
|
||||||
} from "$lib/store";
|
} from "$lib/store";
|
||||||
import { removeCompanyStrings } from "$lib/utils";
|
import { removeCompanyStrings } from "$lib/utils";
|
||||||
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
|
||||||
@ -16,24 +17,11 @@
|
|||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
let isLoaded = true;
|
|
||||||
let tableList = [];
|
let tableList = [];
|
||||||
let processedData = {};
|
let processedData = {};
|
||||||
|
|
||||||
let financialData = [];
|
let financialData = [];
|
||||||
let fullStatement = [];
|
let fullStatement = [];
|
||||||
let filterRule = "annual";
|
|
||||||
|
|
||||||
let activeIdx = 0;
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
title: "Annual",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Quarterly",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const statementConfig = [
|
const statementConfig = [
|
||||||
{
|
{
|
||||||
@ -191,8 +179,8 @@
|
|||||||
|
|
||||||
let properties = [
|
let properties = [
|
||||||
{
|
{
|
||||||
key: filterRule === "annual" ? "fiscalYear" : "date",
|
key: $selectedTimePeriod === "annual" ? "fiscalYear" : "date",
|
||||||
label: filterRule === "annual" ? "Year" : "Quarter",
|
label: $selectedTimePeriod === "annual" ? "Year" : "Quarter",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -225,7 +213,7 @@
|
|||||||
a.href = url;
|
a.href = url;
|
||||||
a.download =
|
a.download =
|
||||||
$stockTicker.toLowerCase() +
|
$stockTicker.toLowerCase() +
|
||||||
`${filterRule === "annual" ? "_annual" : "_quarter"}_cash_flow_statement.csv`;
|
`${$selectedTimePeriod === "annual" ? "_annual" : $selectedTimePeriod === "quarterly" ? "_quarter" : "_ttm"}_ratios.csv`;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
@ -248,13 +236,16 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Precompute xList from income (reverse order)
|
||||||
const xList = [];
|
const xList = [];
|
||||||
for (let i = financialData.length - 1; i >= 0; i--) {
|
for (let i = financialData.length - 1; i >= 0; i--) {
|
||||||
const statement = financialData[i];
|
const statement = financialData[i];
|
||||||
const year = statement.fiscalYear.slice(-2);
|
const year = statement.fiscalYear.slice(-2);
|
||||||
const quarter = statement.period;
|
const quarter = statement.period;
|
||||||
xList.push(
|
xList.push(
|
||||||
filterRule === "annual" ? "FY" + year : "FY" + year + " " + quarter,
|
$selectedTimePeriod === "annual"
|
||||||
|
? "FY" + year
|
||||||
|
: "FY" + year + " " + quarter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +273,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Build tableList once for all charts and sort by date (newest first)
|
// Build tableList once for all charts and sort by date (newest first)
|
||||||
tableList = financialData.map((statement) => ({
|
tableList = financialData?.map((statement) => ({
|
||||||
date: statement.date,
|
date: statement.date,
|
||||||
// Add more properties if needed
|
// Add more properties if needed
|
||||||
}));
|
}));
|
||||||
@ -291,14 +282,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if ($timeFrame || activeIdx) {
|
if ($timeFrame || $selectedTimePeriod) {
|
||||||
if (activeIdx === 0) {
|
if ($selectedTimePeriod === "annual") {
|
||||||
filterRule = "annual";
|
|
||||||
fullStatement = data?.getData?.annual;
|
fullStatement = data?.getData?.annual;
|
||||||
} else {
|
} else if ($selectedTimePeriod === "quarterly") {
|
||||||
filterRule = "quarterly";
|
|
||||||
fullStatement = data?.getData?.quarter;
|
fullStatement = data?.getData?.quarter;
|
||||||
|
} else if ($selectedTimePeriod === "ttm") {
|
||||||
|
fullStatement = data?.getData?.ttm;
|
||||||
|
} else {
|
||||||
|
fullStatement = data?.getData?.annual;
|
||||||
}
|
}
|
||||||
|
|
||||||
financialData = filterStatement(fullStatement, $timeFrame);
|
financialData = filterStatement(fullStatement, $timeFrame);
|
||||||
preprocessFinancialData();
|
preprocessFinancialData();
|
||||||
}
|
}
|
||||||
@ -318,113 +312,70 @@
|
|||||||
<main class="w-full">
|
<main class="w-full">
|
||||||
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
|
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
|
||||||
<div
|
<div
|
||||||
class="mb-3 flex flex-col sm:flex-row items-start sm:items-center justify-between"
|
class="mb-3 sm:mb-0 flex flex-col sm:flex-row items-start sm:items-center justify-between"
|
||||||
>
|
>
|
||||||
<h1 class="text-xl sm:text-2xl font-bold">
|
<h1 class="text-xl sm:text-2xl font-bold">
|
||||||
{removeCompanyStrings($displayCompanyName)} Ratios Statement
|
{removeCompanyStrings($displayCompanyName)} Income Statement
|
||||||
</h1>
|
</h1>
|
||||||
<div
|
<label class="inline-flex sm:hidden mt-4 cursor-pointer relative">
|
||||||
class="mt-3 sm:mt-0 mb-2 sm:mb-0 bg-gray-300 dark:bg-secondary w-full min-w-24 sm:w-fit relative flex flex-wrap items-center justify-center rounded-md p-1"
|
<input
|
||||||
>
|
on:click={toggleMode}
|
||||||
{#each tabs as item, i}
|
type="checkbox"
|
||||||
{#if !["Pro", "Plus"]?.includes(data?.user?.tier) && i > 0}
|
checked={$coolMode}
|
||||||
<button
|
value={$coolMode}
|
||||||
on:click={() => goto("/pricing")}
|
class="sr-only peer"
|
||||||
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
|
/>
|
||||||
>
|
<div
|
||||||
<span class="relative text-sm block font-semibold">
|
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
||||||
{item.title}
|
></div>
|
||||||
<svg
|
{#if $coolMode}
|
||||||
class="inline-block ml-0.5 -mt-1 w-3.5 h-3.5"
|
<span class="ml-2 text-sm"> Table Mode </span>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
{:else}
|
||||||
viewBox="0 0 24 24"
|
<span class="ml-2 text-sm"> Chart Mode </span>
|
||||||
><path
|
{/if}
|
||||||
fill="currentColor"
|
</label>
|
||||||
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
|
|
||||||
>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
on:click={() => (activeIdx = i)}
|
|
||||||
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1 {activeIdx ===
|
|
||||||
i
|
|
||||||
? 'z-0'
|
|
||||||
: ''} "
|
|
||||||
>
|
|
||||||
{#if activeIdx === i}
|
|
||||||
<div class="absolute inset-0 rounded-md bg-[#fff]"></div>
|
|
||||||
{/if}
|
|
||||||
<span
|
|
||||||
class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
|
|
||||||
i
|
|
||||||
? 'text-black'
|
|
||||||
: ''}"
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-2">
|
<div class="grid grid-cols-1 gap-2">
|
||||||
{#if financialData?.length > 0}
|
{#if financialData?.length > 0}
|
||||||
<div
|
<div
|
||||||
class="flex flex-row items-center w-full justify-end sm:justify-center"
|
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
|
||||||
>
|
>
|
||||||
<label
|
<span
|
||||||
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
|
class="text-xs sm:text-sm order-1 sm:order-0 mt-5 sm:mt-0 text-gray-600 dark:text-gray-400 w-full"
|
||||||
>
|
>
|
||||||
<input
|
Financials in {financialData?.at(0)?.reportedCurrency}. Fiscal
|
||||||
on:click={toggleMode}
|
year is
|
||||||
type="checkbox"
|
{data?.getProfileData?.fiscalYearRange}.
|
||||||
checked={$coolMode}
|
</span>
|
||||||
value={$coolMode}
|
<div class="flex flex-row items-center justify-end w-full">
|
||||||
class="sr-only peer"
|
<label
|
||||||
/>
|
class="hidden sm:inline-flex ml-auto mt-2 sm:mt-0 cursor-pointer relative"
|
||||||
<div
|
>
|
||||||
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
<input
|
||||||
></div>
|
on:click={toggleMode}
|
||||||
{#if $coolMode}
|
type="checkbox"
|
||||||
<span class="ml-2 text-sm"> Table Mode </span>
|
checked={$coolMode}
|
||||||
{:else}
|
value={$coolMode}
|
||||||
<span class="ml-2 text-sm"> Chart Mode </span>
|
class="sr-only peer"
|
||||||
{/if}
|
/>
|
||||||
</label>
|
<div
|
||||||
|
class="w-11 h-6 bg-gray-400 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#1563F9]"
|
||||||
<div class="flex flex-row items-center w-fit sm:ml-auto">
|
></div>
|
||||||
<!--
|
{#if $coolMode}
|
||||||
<div class="ml-2 relative inline-block text-left">
|
<span class="ml-2 text-sm"> Table Mode </span>
|
||||||
<Button
|
{:else}
|
||||||
class="shadow-sm w-full border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-5 rounded-md truncate"
|
<span class="ml-2 text-sm"> Chart Mode </span>
|
||||||
>
|
{/if}
|
||||||
<svg
|
</label>
|
||||||
class="w-4 h-4 bp:w-5 bp:h-5 pointer-events-none"
|
<div class="ml-auto sm:ml-5 relative inline-block">
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
style="max-width:40px"
|
|
||||||
><path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"
|
|
||||||
></path></svg
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ml-2 relative inline-block text-left grow">
|
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
<DropdownMenu.Trigger asChild let:builder>
|
||||||
<Button
|
<Button
|
||||||
builders={[builder]}
|
builders={[builder]}
|
||||||
class="shadow-sm w-full border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
class="shadow-sm w-full sm:w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
||||||
>
|
>
|
||||||
<span class="truncate">Raw</span>
|
<span class="truncate">{$timeFrame}</span>
|
||||||
<svg
|
<svg
|
||||||
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@ -443,33 +394,55 @@
|
|||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
||||||
>
|
>
|
||||||
|
<DropdownMenu.Label
|
||||||
|
class="text-muted dark:text-gray-400"
|
||||||
|
>
|
||||||
|
Select time frame
|
||||||
|
</DropdownMenu.Label>
|
||||||
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "5Y")}
|
on:click={() => ($timeFrame = "5Y")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Billions
|
5 years
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "10Y")}
|
on:click={() => ($timeFrame = "10Y")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Millions
|
10 years
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
on:click={() => ($timeFrame = "MAX")}
|
on:click={() => ($timeFrame = "MAX")}
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
||||||
>
|
>
|
||||||
Raw
|
Max
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
|
<Button
|
||||||
|
on:click={() => exportFundamentalData("csv")}
|
||||||
|
class="shadow-sm ml-2 w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
||||||
|
>
|
||||||
|
<span class="truncate">Download</span>
|
||||||
|
<svg
|
||||||
|
class="{['Pro', 'Plus']?.includes(data?.user?.tier)
|
||||||
|
? 'hidden'
|
||||||
|
: ''} ml-1 -mt-0.5 w-3.5 h-3.5"
|
||||||
|
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
|
||||||
|
>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $coolMode}
|
{#if $coolMode}
|
||||||
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
|
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
|
||||||
{#each fields as item, i}
|
{#each fields as item, i}
|
||||||
@ -477,7 +450,7 @@
|
|||||||
data={financialData}
|
data={financialData}
|
||||||
{statementConfig}
|
{statementConfig}
|
||||||
displayStatement={item?.key}
|
displayStatement={item?.key}
|
||||||
{filterRule}
|
filterRule={$selectedTimePeriod}
|
||||||
{processedData}
|
{processedData}
|
||||||
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
|
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
|
||||||
i % 4
|
i % 4
|
||||||
@ -486,91 +459,6 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
|
||||||
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="text-xs sm:text-sm order-1 sm:order-0 mt-5 sm:mt-0 text-gray-600 dark:text-gray-400 w-full"
|
|
||||||
>
|
|
||||||
Financials in {data?.getProfileData?.currency}. Fiscal year
|
|
||||||
is
|
|
||||||
{data?.getProfileData?.fiscalYearRange}.
|
|
||||||
</span>
|
|
||||||
<div class="flex flex-row items-center justify-end w-full">
|
|
||||||
<div class="sm:ml-auto relative inline-block">
|
|
||||||
<DropdownMenu.Root>
|
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
|
||||||
<Button
|
|
||||||
builders={[builder]}
|
|
||||||
class="shadow-sm w-full sm:w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
|
||||||
>
|
|
||||||
<span class="truncate">{$timeFrame}</span>
|
|
||||||
<svg
|
|
||||||
class="-mr-1 ml-1 h-5 w-5 xs:ml-2 inline-block"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
style="max-width:40px"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenu.Trigger>
|
|
||||||
<DropdownMenu.Content
|
|
||||||
class="w-56 h-fit max-h-72 overflow-y-auto scroller"
|
|
||||||
>
|
|
||||||
<DropdownMenu.Label
|
|
||||||
class="text-muted dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Select time frame
|
|
||||||
</DropdownMenu.Label>
|
|
||||||
<DropdownMenu.Separator />
|
|
||||||
<DropdownMenu.Group>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "5Y")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
5 years
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "10Y")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
10 years
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
on:click={() => ($timeFrame = "MAX")}
|
|
||||||
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
|
|
||||||
>
|
|
||||||
Max
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</DropdownMenu.Group>
|
|
||||||
</DropdownMenu.Content>
|
|
||||||
</DropdownMenu.Root>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
on:click={() => exportFundamentalData("csv")}
|
|
||||||
class="shadow-sm ml-2 w-fit border-gray-300 dark:border-gray-600 border sm:hover:bg-gray-100 dark:sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-1.5 rounded-md truncate"
|
|
||||||
>
|
|
||||||
<span class="truncate">Download</span>
|
|
||||||
<svg
|
|
||||||
class="{['Pro', 'Plus']?.includes(data?.user?.tier)
|
|
||||||
? 'hidden'
|
|
||||||
: ''} ml-1 -mt-0.5 w-3.5 h-3.5"
|
|
||||||
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
|
|
||||||
>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
|
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
|
||||||
>
|
>
|
||||||
@ -583,14 +471,16 @@
|
|||||||
>Fiscal Year</td
|
>Fiscal Year</td
|
||||||
>
|
>
|
||||||
{#each financialData as item}
|
{#each financialData as item}
|
||||||
{#if filterRule === "annual"}
|
{#if $selectedTimePeriod === "annual"}
|
||||||
<td
|
<td
|
||||||
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
|
class="min-w-[130px] font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
|
||||||
>
|
>
|
||||||
{"FY" + " " + item?.fiscalYear}
|
{"FY" + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td class=" font-semibold text-sm text-end">
|
<td
|
||||||
|
class="min-w-[130px] font-semibold text-sm text-end"
|
||||||
|
>
|
||||||
{item?.period + " " + item?.fiscalYear}
|
{item?.period + " " + item?.fiscalYear}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user