This commit is contained in:
MuslemRahimi 2025-04-10 12:58:33 +02:00
parent 03e62af963
commit 6ff737218a
9 changed files with 508 additions and 90 deletions

View File

@ -0,0 +1,425 @@
<script lang="ts">
import {
displayCompanyName,
stockTicker,
coolMode,
timeFrame,
selectedTimePeriod,
screenWidth,
} from "$lib/store";
import { removeCompanyStrings } from "$lib/utils";
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
import { Button } from "$lib/components/shadcn/button/index.js";
//import * as XLSX from 'xlsx';
import FinancialTable from "$lib/components/FinancialTable.svelte";
import FinancialChart from "$lib/components/FinancialChart.svelte";
import TableMode from "lucide-svelte/icons/grid-2x2";
import ChartMode from "lucide-svelte/icons/chart-column-increasing";
import Download from "lucide-svelte/icons/download";
import { goto } from "$app/navigation";
import SEO from "$lib/components/SEO.svelte";
export let data;
export let title;
export let statementConfig;
let seoDescription = "";
if (title === "Income Statement") {
seoDescription = `Detailed annual, quarterly and trailing income statement for ${$displayCompanyName} (${$stockTicker}). See many years of revenue, expenses and profits or losses.`;
}
let switchDate = false;
let tableList = [];
let processedData = {};
let financialData = [];
let fullStatement = [];
const fields = statementConfig.map((item) => ({
label: item.label,
key: item.propertyName,
}));
function toggleMode() {
$coolMode = !$coolMode;
}
const getCurrentYear = () => new Date()?.getFullYear();
const filterStatement = (fullStatement, timeFrame, switchDate) => {
const currentYear = getCurrentYear();
let filtered = [...(fullStatement || [])];
switch (timeFrame) {
case "5Y":
filtered = filtered.filter(
(item) => currentYear - parseInt(item?.fiscalYear) < 5,
);
break;
case "10Y":
filtered = filtered.filter(
(item) => currentYear - parseInt(item?.fiscalYear) < 10,
);
break;
}
// Sort by date depending on switchDate
filtered.sort((a, b) => {
const dateA = new Date(a.date || a.fiscalDate || a.fiscalYear);
const dateB = new Date(b.date || b.fiscalDate || b.fiscalYear);
return switchDate
? dateA - dateB // earliest to latest
: dateB - dateA; // latest to earliest
});
return filtered;
};
fullStatement = data?.getData;
const exportFundamentalData = (format = "csv") => {
if (["Pro", "Plus"]?.includes(data?.user?.tier)) {
const data = fullStatement;
if (!data || data.length === 0) {
return;
}
let properties = [
{
key: $selectedTimePeriod === "annual" ? "fiscalYear" : "date",
label: $selectedTimePeriod === "annual" ? "Year" : "Quarter",
},
];
for (let i = 0; i < statementConfig?.length; i++) {
properties.push({
key: statementConfig[i]?.propertyName,
label: statementConfig[i]?.label,
});
}
// Helper function to handle special cases
// Create rows for CSV/Excel
let rows = data.map((item) =>
properties?.map((property) => item[property?.key] || 0),
);
// Include headers
const headers = properties.map((prop) => prop.label);
rows.unshift(headers);
// Check the format to export
if (format.toLowerCase() === "csv") {
const csvContent = rows.map((row) => row.join(",")).join("\n");
const blob = new Blob([csvContent], {
type: "data:text/csv;charset=utf-8",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download =
$stockTicker.toLowerCase() +
`${$selectedTimePeriod === "annual" ? "_annual" : $selectedTimePeriod === "quarterly" ? "_quarter" : "_ttm"}_income_statement.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
} else {
goto("/pricing");
}
};
// Pre-process all data once instead of in each component
function preprocessFinancialData() {
processedData = {};
// Precompute mapping from propertyName to config for quick lookup
const configMap = {};
statementConfig.forEach((item) => {
if (item && item.propertyName) {
configMap[item.propertyName] = item;
}
});
// Precompute xList from income (reverse order)
const xList = [];
for (let i = financialData.length - 1; i >= 0; i--) {
const statement = financialData[i];
const year = statement.fiscalYear.slice(-2);
const quarter = statement.period;
xList.push(
$selectedTimePeriod === "annual"
? "FY" + year
: "FY" + year + " " + quarter,
);
}
// Process each field using precomputed config and xList
fields.forEach((field) => {
const statementKey = field.key;
const config = configMap[statementKey];
if (!config) return;
const valueList = [];
// Loop through financialData in reverse to match xList order
for (let i = financialData.length - 1; i >= 0; i--) {
const statement = financialData[i];
const rawValue = Number(statement[config.propertyName]);
// Round to two decimals
const value = parseFloat(rawValue.toFixed(2));
valueList.push(value);
}
processedData[statementKey] = {
xList, // re-use the precomputed labels
valueList,
labelName: config.label,
};
});
// Build tableList once for all charts and sort by date (newest first)
tableList = financialData?.map((statement) => ({
date: statement.date,
// Add more properties if needed
}));
tableList.sort((a, b) => new Date(b.date) - new Date(a.date));
}
$: {
if ($timeFrame || $selectedTimePeriod) {
if ($selectedTimePeriod === "annual") {
fullStatement = data?.getData?.annual;
} else if ($selectedTimePeriod === "quarterly") {
fullStatement = data?.getData?.quarter;
} else if ($selectedTimePeriod === "ttm") {
fullStatement = data?.getData?.ttm;
} else {
fullStatement = data?.getData?.annual;
}
financialData = filterStatement(fullStatement, $timeFrame, switchDate);
preprocessFinancialData();
}
}
</script>
<SEO
title={`${$displayCompanyName} (${$stockTicker}) Financials - ${title}`}
description={seoDescription}
/>
<section class=" w-full overflow-hidden h-full">
<div class="w-full flex justify-center w-full sm-auto h-full overflow-hidden">
<div
class="w-full relative flex justify-center items-center overflow-hidden"
>
<main class="w-full">
<div class="sm:pl-7 sm:pb-7 sm:pt-7 m-auto mt-2 sm:mt-0">
<div
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">
{removeCompanyStrings($displayCompanyName)}
{title}
</h1>
</div>
<div class="grid grid-cols-1 gap-2">
{#if financialData?.length > 0}
<div class="flex flex-col md:flex-row items-end 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 {financialData?.at(0)?.reportedCurrency}. Fiscal
year is
{data?.getProfileData?.fiscalYearRange}.
</span>
<div class="flex flex-row items-center justify-end w-full">
<Button
on:click={toggleMode}
class=" shadow-sm w-full max-w-36 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-4 py-1.5 rounded-md truncate"
>
{#if $coolMode}
<TableMode class="w-4.5 h-4.5" />
<span class="ml-2 text-sm"> Table Mode </span>
{:else}
<ChartMode class="w-4.5 h-4.5" />
<span class="ml-2 text-sm"> Chart Mode </span>
{/if}</Button
>
<Button
on:click={() => (switchDate = !switchDate)}
class="ml-2 shadow-sm w-48 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-4 py-1.5 rounded-md truncate"
>
<svg
class="shrink-0 w-5 h-5 pointer-events-none m-auto"
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 class="ml-2 relative inline-block">
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild let:builder>
<Button
builders={[builder]}
class="flex-shrink-0 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 font-normal"
>
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-20 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"
>
{#if $screenWidth < 640}
<Download class="w-4.5 h-4.5 flex-shrink-0 m-auto" />
{:else}
<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
>
{/if}
</Button>
</div>
</div>
{#if $coolMode}
<div class="grid gap-5 xs:gap-6 lg:grid-cols-3 lg:gap-3">
{#each fields as item, i}
<FinancialChart
data={financialData}
{statementConfig}
displayStatement={item?.key}
filterRule={$selectedTimePeriod}
{processedData}
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
i % 4
]}
/>
{/each}
</div>
{:else}
<div
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
>
<table
class="table table-sm table-compact no-scrollbar rounded-none sm:rounded-md w-full border border-gray-300 dark:border-gray-800 m-auto"
>
<thead class="text-muted dark:text-white dark:bg-default">
<tr class="border min-w-[250px]">
<td class="text-start text-sm font-semibold pr-10"
>Fiscal Year</td
>
{#each financialData as item}
{#if $selectedTimePeriod === "annual"}
<td
class="min-w-[130px] font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
>
{"FY" + " " + item?.fiscalYear}
</td>
{:else}
<td
class="min-w-[130px] font-semibold text-sm text-end"
>
{item?.period + " " + item?.fiscalYear}
</td>
{/if}
{/each}
</tr>
<tr class="border min-w-[250px]">
<td class="text-start text-sm font-semibold"
>Period Ending</td
>
{#each financialData as item}
<td
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
>
{new Date(item?.date).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</td>
{/each}
</tr>
</thead>
<tbody>
<!-- row -->
<FinancialTable data={financialData} {fields} />
</tbody>
</table>
</div>
{/if}
{/if}
</div>
</div>
</main>
</div>
</div>
</section>

View File

@ -264,10 +264,7 @@
{/each} {/each}
<input type="checkbox" id="financialPlotModal" class="modal-toggle" /> <input type="checkbox" id="financialPlotModal" class="modal-toggle" />
<dialog <dialog id="financialPlotModal" class="modal bg-[#000]/40 px-3">
id="financialPlotModal"
class="modal {$screenWidth < 640 ? 'modal-bottom ' : ''} bg-[#000]/40 sm:px-5"
>
<label for="financialPlotModal" class="cursor-pointer modal-backdrop"></label> <label for="financialPlotModal" class="cursor-pointer modal-backdrop"></label>
<div <div

View File

@ -15,7 +15,7 @@ const buttonVariants = tv({
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {
default: "h-10 px-4 py-2", default: "h-9 px-4 py-2",
sm: "h-9 rounded-md px-3", sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8", lg: "h-11 rounded-md px-8",
icon: "h-10 w-10", icon: "h-10 w-10",

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import "../app.css"; import "../app.css";
import "../app.pcss"; import "../app.pcss";
//import { partytownSnippet } from "@builder.io/partytown/integration"; import { partytownSnippet } from "@builder.io/partytown/integration";
import { Toaster } from "svelte-sonner"; import { Toaster } from "svelte-sonner";
import "@bprogress/core/css"; import "@bprogress/core/css";
import { BProgress } from "@bprogress/core"; import { BProgress } from "@bprogress/core";
@ -248,7 +248,7 @@
<svelte:window bind:innerWidth={$screenWidth} /> <svelte:window bind:innerWidth={$screenWidth} />
<!--
<svelte:head> <svelte:head>
<script> <script>
// Forward the necessary functions to the web worker layer // Forward the necessary functions to the web worker layer
@ -273,7 +273,7 @@
</script> </script>
</svelte:head> </svelte:head>
-->
<ModeWatcher defaultMode={data?.themeMode} /> <ModeWatcher defaultMode={data?.themeMode} />

View File

@ -5,6 +5,7 @@
coolMode, coolMode,
timeFrame, timeFrame,
selectedTimePeriod, selectedTimePeriod,
screenWidth,
} 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";
@ -12,11 +13,16 @@
//import * as XLSX from 'xlsx'; //import * as XLSX from 'xlsx';
import FinancialTable from "$lib/components/FinancialTable.svelte"; import FinancialTable from "$lib/components/FinancialTable.svelte";
import FinancialChart from "$lib/components/FinancialChart.svelte"; import FinancialChart from "$lib/components/FinancialChart.svelte";
import TableMode from "lucide-svelte/icons/grid-2x2";
import ChartMode from "lucide-svelte/icons/chart-column-increasing";
import Download from "lucide-svelte/icons/download";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import SEO from "$lib/components/SEO.svelte"; import SEO from "$lib/components/SEO.svelte";
export let data; export let data;
let switchDate = false;
let tableList = []; let tableList = [];
let processedData = {}; let processedData = {};
@ -125,21 +131,34 @@
const getCurrentYear = () => new Date()?.getFullYear(); const getCurrentYear = () => new Date()?.getFullYear();
const filterStatement = (fullStatement, timeFrame) => { const filterStatement = (fullStatement, timeFrame, switchDate) => {
const currentYear = getCurrentYear(); const currentYear = getCurrentYear();
let filtered = [...(fullStatement || [])];
switch (timeFrame) { switch (timeFrame) {
case "5Y": case "5Y":
return fullStatement?.filter( filtered = filtered.filter(
(item) => currentYear - parseInt(item?.fiscalYear) < 5, (item) => currentYear - parseInt(item?.fiscalYear) < 5,
); );
break;
case "10Y": case "10Y":
return fullStatement?.filter( filtered = filtered.filter(
(item) => currentYear - parseInt(item?.fiscalYear) < 10, (item) => currentYear - parseInt(item?.fiscalYear) < 10,
); );
default: break;
return fullStatement;
} }
// Sort by date depending on switchDate
filtered.sort((a, b) => {
const dateA = new Date(a.date || a.fiscalDate || a.fiscalYear);
const dateB = new Date(b.date || b.fiscalDate || b.fiscalYear);
return switchDate
? dateA - dateB // earliest to latest
: dateB - dateA; // latest to earliest
});
return filtered;
}; };
fullStatement = data?.getData; fullStatement = data?.getData;
@ -267,7 +286,7 @@
fullStatement = data?.getData?.annual; fullStatement = data?.getData?.annual;
} }
financialData = filterStatement(fullStatement, $timeFrame); financialData = filterStatement(fullStatement, $timeFrame, switchDate);
preprocessFinancialData(); preprocessFinancialData();
} }
} }
@ -291,30 +310,11 @@
<h1 class="text-xl sm:text-2xl font-bold"> <h1 class="text-xl sm:text-2xl font-bold">
{removeCompanyStrings($displayCompanyName)} Income Statement {removeCompanyStrings($displayCompanyName)} Income Statement
</h1> </h1>
<label class="inline-flex sm:hidden mt-4 cursor-pointer relative">
<input
on:click={toggleMode}
type="checkbox"
checked={$coolMode}
value={$coolMode}
class="sr-only peer"
/>
<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>
{#if $coolMode}
<span class="ml-2 text-sm"> Table Mode </span>
{:else}
<span class="ml-2 text-sm"> Chart Mode </span>
{/if}
</label>
</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-col md:flex-row items-end justify-between">
class="flex flex-col sm:flex-row items-start sm:items-end sm:justify-between"
>
<span <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" 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"
> >
@ -323,31 +323,43 @@
{data?.getProfileData?.fiscalYearRange}. {data?.getProfileData?.fiscalYearRange}.
</span> </span>
<div class="flex flex-row items-center justify-end w-full"> <div class="flex flex-row items-center justify-end w-full">
<label <Button
class="hidden sm:inline-flex ml-auto mt-2 sm:mt-0 cursor-pointer relative"
>
<input
on:click={toggleMode} on:click={toggleMode}
type="checkbox" class=" shadow-sm w-full max-w-36 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-4 py-1.5 rounded-md truncate"
checked={$coolMode} >
value={$coolMode}
class="sr-only peer"
/>
<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>
{#if $coolMode} {#if $coolMode}
<TableMode class="w-4.5 h-4.5" />
<span class="ml-2 text-sm"> Table Mode </span> <span class="ml-2 text-sm"> Table Mode </span>
{:else} {:else}
<ChartMode class="w-4.5 h-4.5" />
<span class="ml-2 text-sm"> Chart Mode </span> <span class="ml-2 text-sm"> Chart Mode </span>
{/if} {/if}</Button
</label> >
<div class="ml-auto sm:ml-5 relative inline-block">
<Button
on:click={() => (switchDate = !switchDate)}
class="ml-2 shadow-sm w-48 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-4 py-1.5 rounded-md truncate"
>
<svg
class="shrink-0 w-5 h-5 pointer-events-none m-auto"
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 class="ml-2 relative inline-block">
<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 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" class="flex-shrink-0 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> <span class="truncate">{$timeFrame}</span>
<svg <svg
@ -369,7 +381,7 @@
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 <DropdownMenu.Label
class="text-muted dark:text-gray-400" class="text-muted dark:text-gray-400 font-normal"
> >
Select time frame Select time frame
</DropdownMenu.Label> </DropdownMenu.Label>
@ -400,8 +412,11 @@
<Button <Button
on:click={() => exportFundamentalData("csv")} 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" class="shadow-sm ml-2 w-20 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"
> >
{#if $screenWidth < 640}
<Download class="w-4.5 h-4.5 flex-shrink-0 m-auto" />
{:else}
<span class="truncate">Download</span> <span class="truncate">Download</span>
<svg <svg
class="{['Pro', 'Plus']?.includes(data?.user?.tier) class="{['Pro', 'Plus']?.includes(data?.user?.tier)
@ -414,6 +429,7 @@
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" 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 /></svg
> >
{/if}
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -442,7 +442,7 @@
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 <DropdownMenu.Label
class="text-muted dark:text-gray-400" class="text-muted dark:text-gray-400 font-normal"
> >
Select time frame Select time frame
</DropdownMenu.Label> </DropdownMenu.Label>

View File

@ -26,131 +26,111 @@
const statementConfig = [ const statementConfig = [
{ {
propertyName: "netIncome", propertyName: "netIncome",
growthPropertyName: "growthNetIncome",
label: "Net Income", label: "Net Income",
text: "Net income is a company's accounting profits after subtracting all costs and expenses from the revenue. It is also called earnings, profits.", text: "Net income is a company's accounting profits after subtracting all costs and expenses from the revenue. It is also called earnings, profits.",
}, },
{ {
propertyName: "depreciationAndAmortization", propertyName: "depreciationAndAmortization",
growthPropertyName: "growthDepreciationAndAmortization",
label: "Depreciation & Amortization", label: "Depreciation & Amortization",
text: "Depreciation and amortization are accounting methods for calculating how the value of a business's assets change over time. Depreciation refers to physical assets, while amortization refers to intangible assets.", text: "Depreciation and amortization are accounting methods for calculating how the value of a business's assets change over time. Depreciation refers to physical assets, while amortization refers to intangible assets.",
}, },
{ {
propertyName: "stockBasedCompensation", propertyName: "stockBasedCompensation",
growthPropertyName: "growthStockBasedCompensation",
label: "Stock-Based Compensation", label: "Stock-Based Compensation",
text: "Stock-based compensation is the value of stocks issued for the purpose of compensating the executives and employees of a company.", text: "Stock-based compensation is the value of stocks issued for the purpose of compensating the executives and employees of a company.",
}, },
{ {
propertyName: "otherWorkingCapital", propertyName: "otherWorkingCapital",
growthPropertyName: "growthOtherWorkingCapital",
label: "Other Working Capital", label: "Other Working Capital",
text: "Other working capital represents miscellaneous changes in short-term assets and liabilities that don't have specific categories, affecting the company's available cash.", text: "Other working capital represents miscellaneous changes in short-term assets and liabilities that don't have specific categories, affecting the company's available cash.",
}, },
{ {
propertyName: "otherNonCashItems", propertyName: "otherNonCashItems",
growthPropertyName: "growthOtherNonCashItems",
label: "Other Non-Cash Items", label: "Other Non-Cash Items",
text: "Other Non-Cash Items refers to non-cash transactions or adjustments that impact the company's financial performance but don't involve actual cash flows. These can include items like depreciation, amortization, or changes in the fair value of investments.", text: "Other Non-Cash Items refers to non-cash transactions or adjustments that impact the company's financial performance but don't involve actual cash flows. These can include items like depreciation, amortization, or changes in the fair value of investments.",
}, },
{ {
propertyName: "deferredIncomeTax", propertyName: "deferredIncomeTax",
growthPropertyName: "growthDeferredIncomeTax",
label: "Deferred Income Tax", label: "Deferred Income Tax",
text: "Deferred income tax refers to future tax liabilities or assets resulting from differences in how a company reports income for tax purposes versus financial reporting. It represents the amount of income tax that will be paid or saved in the future due to these differences.", text: "Deferred income tax refers to future tax liabilities or assets resulting from differences in how a company reports income for tax purposes versus financial reporting. It represents the amount of income tax that will be paid or saved in the future due to these differences.",
}, },
{ {
propertyName: "changeInWorkingCapital", propertyName: "changeInWorkingCapital",
growthPropertyName: "growthChangeInWorkingCapital",
label: "Change in Working Capital", label: "Change in Working Capital",
text: "Change in working capital is the difference between a company's short-term assets and liabilities over a specific period, reflecting how much cash flow is impacted by these changes.", text: "Change in working capital is the difference between a company's short-term assets and liabilities over a specific period, reflecting how much cash flow is impacted by these changes.",
}, },
{ {
propertyName: "netCashProvidedByOperatingActivities", propertyName: "netCashProvidedByOperatingActivities",
growthPropertyName: "growthNetCashProvidedByOperatingActivites",
label: "Operating Cash Flow", label: "Operating Cash Flow",
text: "Operating cash flow, also called cash flow from operating activities, measures the amount of cash that a company generates from normal business activities. It is the amount of cash left after all cash income has been received, and all cash expenses have been paid.", text: "Operating cash flow, also called cash flow from operating activities, measures the amount of cash that a company generates from normal business activities. It is the amount of cash left after all cash income has been received, and all cash expenses have been paid.",
}, },
{ {
propertyName: "capitalExpenditure", propertyName: "capitalExpenditure",
growthPropertyName: "growthCapitalExpenditure",
label: "Capital Expenditures", label: "Capital Expenditures",
text: "Capital expenditures are also called payments for property, plants and equipment. It measures cash spent on long-term assets that will be used to run the business, such as manufacturing equipment, real estate and others.", text: "Capital expenditures are also called payments for property, plants and equipment. It measures cash spent on long-term assets that will be used to run the business, such as manufacturing equipment, real estate and others.",
}, },
{ {
propertyName: "acquisitionsNet", propertyName: "acquisitionsNet",
growthPropertyName: "growthAcquisitionsNet", label: "Cash Acquisitions",
label: "Acquisitions",
text: "The amount of cash spent on acquiring other businesses.", text: "The amount of cash spent on acquiring other businesses.",
}, },
{ {
propertyName: "purchasesOfInvestments", propertyName: "purchasesOfInvestments",
growthPropertyName: "growthPurchasesOfInvestments",
label: "Purchase of Investments", label: "Purchase of Investments",
text: "Purchase of Investments refers to the acquisition of financial assets like stocks or bonds by a company, typically for investment purposes. It represents an outflow of cash used to buy these investments.", text: "Purchase of Investments refers to the acquisition of financial assets like stocks or bonds by a company, typically for investment purposes. It represents an outflow of cash used to buy these investments.",
}, },
{ {
propertyName: "salesMaturitiesOfInvestments", propertyName: "salesMaturitiesOfInvestments",
growthPropertyName: "growthSalesMaturitiesOfInvestments",
label: "Sales Maturities Of Investments", label: "Sales Maturities Of Investments",
text: "Sales Maturities of Investments signifies the selling or maturity of financial assets like stocks or bonds by a company. It represents an inflow of cash resulting from these investment activities.", text: "Sales Maturities of Investments signifies the selling or maturity of financial assets like stocks or bonds by a company. It represents an inflow of cash resulting from these investment activities.",
}, },
{ {
propertyName: "otherInvestingActivities", propertyName: "otherInvestingActivities",
growthPropertyName: "growthOtherInvestingActivites",
label: "Other Investing Acitivies", label: "Other Investing Acitivies",
text: "Other investing activities are investing activities that do not belong to any of the categories we mentioned so far.", text: "Other investing activities are investing activities that do not belong to any of the categories we mentioned so far.",
}, },
{ {
propertyName: "netCashProvidedByInvestingActivities", propertyName: "netCashProvidedByInvestingActivities",
growthPropertyName: "growthNetCashUsedForInvestingActivites",
label: "Investing Cash Flow", label: "Investing Cash Flow",
text: "Investing cash flow is the total change in cash from buying and selling investments and long-term assets.", text: "Investing cash flow is the total change in cash from buying and selling investments and long-term assets.",
}, },
{ {
propertyName: "netDebtIssuance", propertyName: "netDebtIssuance",
growthPropertyName: "growthDebtRepayment",
label: "Debt Repayment", label: "Debt Repayment",
text: "Debt Repayment is the process of paying off loans or debt obligations. It represents an outflow of cash as the company reduces its outstanding debt.", text: "Debt Repayment is the process of paying off loans or debt obligations. It represents an outflow of cash as the company reduces its outstanding debt.",
}, },
{ {
propertyName: "commonStockRepurchased", propertyName: "commonStockRepurchased",
growthPropertyName: "growthCommonStockRepurchased",
label: "Common Stock Repurchased", label: "Common Stock Repurchased",
text: "The cash gained from issuing shares, or cash spent on repurchasing shares via share buybacks. A positive number implies that the company issued more shares than it repurchased. A negative number implies that the company bought back shares.", text: "The cash gained from issuing shares, or cash spent on repurchasing shares via share buybacks. A positive number implies that the company issued more shares than it repurchased. A negative number implies that the company bought back shares.",
}, },
{ {
propertyName: "netDividendsPaid", propertyName: "netDividendsPaid",
growthPropertyName: "growthDividendsPaid",
label: "Dividend Paid", label: "Dividend Paid",
text: "The total amount paid out as cash dividends to shareholders.", text: "The total amount paid out as cash dividends to shareholders.",
}, },
{ {
propertyName: "otherFinancingActivities", propertyName: "otherFinancingActivities",
growthPropertyName: "growthOtherFinancingActivities",
label: "Other Financial Acitivies", label: "Other Financial Acitivies",
text: "Other financial activities includes miscellaneous financial transactions beyond regular operations that impact a company's cash flow.", text: "Other financial activities includes miscellaneous financial transactions beyond regular operations that impact a company's cash flow.",
}, },
{ {
propertyName: "netCashProvidedByFinancingActivities", propertyName: "netCashProvidedByFinancingActivities",
growthPropertyName: "growthNetCashProvidedByFinancingActivities",
label: "Financial Cash Flow", label: "Financial Cash Flow",
text: "Financing cash flow is the total change in cash through financing activities. This includes dividend payments, share issuance and repurchases, changes in debt levels and others.", text: "Financing cash flow is the total change in cash through financing activities. This includes dividend payments, share issuance and repurchases, changes in debt levels and others.",
}, },
{ {
propertyName: "netChangeInCash", propertyName: "netChangeInCash",
growthPropertyName: "growthNetChangeInCash",
label: "Net Cash Flow", label: "Net Cash Flow",
text: "Net cash flow is the sum of the operating, investing and financing cash flow numbers. It is the change in cash and equivalents on the company's balance sheet during the accounting period. It is often shown as increase/decrease in cash and equivalents on the cash flow statement.", text: "Net cash flow is the sum of the operating, investing and financing cash flow numbers. It is the change in cash and equivalents on the company's balance sheet during the accounting period. It is often shown as increase/decrease in cash and equivalents on the cash flow statement.",
}, },
{ {
propertyName: "freeCashFlow", propertyName: "freeCashFlow",
growthPropertyName: "growthFreeCashFlow",
label: "Free Cash Flow", label: "Free 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.", 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,
@ -405,7 +385,7 @@
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 <DropdownMenu.Label
class="text-muted dark:text-gray-400" class="text-muted dark:text-gray-400 font-normal"
> >
Select time frame Select time frame
</DropdownMenu.Label> </DropdownMenu.Label>

View File

@ -395,7 +395,7 @@
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 <DropdownMenu.Label
class="text-muted dark:text-gray-400" class="text-muted dark:text-gray-400 font-normal"
> >
Select time frame Select time frame
</DropdownMenu.Label> </DropdownMenu.Label>

View File

@ -576,7 +576,7 @@
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 <DropdownMenu.Label
class="text-muted dark:text-gray-400" class="text-muted dark:text-gray-400 font-normal"
> >
Select time frame Select time frame
</DropdownMenu.Label> </DropdownMenu.Label>