update financial tab

This commit is contained in:
MuslemRahimi 2025-04-07 12:59:04 +02:00
parent 709a796285
commit 7fe8bb2911
5 changed files with 202 additions and 209 deletions

View File

@ -199,10 +199,10 @@
{#each computedFields as { label, key, isMargin } (key)}
<tr
class="dark:sm:hover:bg-[#245073]/10 odd:bg-[#F6F7F8] dark:odd:bg-odd whitespace-nowrap"
class="dark:sm:hover:bg-[#245073]/10 odd:bg-[#F6F7F8] dark:odd:bg-odd whitespace-nowrap border"
>
<td
class="text-start min-w-[220px] sm:min-w-[320px] border-r border-gray-300 dark:border-gray-700 text-sm sm:text-[1rem] w-full flex flex-row items-center justify-between"
class="text-start min-w-[220px] sm:min-w-[320px] text-sm sm:text-[1rem] border-r border-gray-300 dark:border-gray-800 w-full flex flex-row items-center justify-between"
>
<label
for="tooltipModal"
@ -248,7 +248,9 @@
</label>
</td>
{#each data as item}
<td class="text-sm sm:text-[1rem] text-end">
<td
class="text-sm sm:text-[1rem] text-end border-r border-gray-300 dark:border-gray-800"
>
{formatValue(item[key], isMargin)}
</td>
{/each}
@ -263,7 +265,7 @@
<label for="financialPlotModal" class="cursor-pointer modal-backdrop"></label>
<div
class="modal-box w-full p-6 rounded shadow-lg border
class="modal-box w-full max-w-3xl p-6 rounded shadow-lg border
bg-white dark:bg-secondary border border-gray-600 dark:border-gray-800"
>
{#if config}
@ -289,12 +291,12 @@
</dialog>
<input type="checkbox" id="tooltipModal" class="modal-toggle" />
<dialog id="tooltipModal" class="modal p-3">
<dialog id="tooltipModal" class="modal p-3 w-full">
<label for="tooltipModal" class="cursor-pointer modal-backdrop"></label>
<!-- Desktop modal content -->
<div
class="modal-box rounded-md border border-gray-300 dark:border-gray-600 w-full bg-white dark:bg-secondary flex flex-col items-center"
class="modal-box w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-secondary flex flex-col items-center"
>
<div class=" mb-5 text-center">
<h3 class="font-bold text-2xl mb-5">{modalLabel}</h3>

View File

@ -47,7 +47,9 @@ export const clearCache = () => {
export const showCookieConsent = writable(<boolean>false);
export const shouldUpdatePriceChart = writable(<boolean>false);
export const selectedTimePeriod = writable(<string>"");
export const coolMode = writable(<boolean>false);
export const timeFrame =writable(<string>"Max");
export const closedPWA = writable(<boolean>false);

View File

@ -19,7 +19,7 @@ type FlyAndScaleParams = {
};
export function removeCompanyStrings(name) {
const wordsToRemove = ["Technologies", "Inc.","Corp.","Corporation","Holding","Limited","Group","N.V."];
const wordsToRemove = ["Technologies", "Inc.","Corp.","Corporation","Holding","Limited","Group","N.V.","Co. Ltd.","Co.", "Ltd."];
if (!name) return "";
return wordsToRemove?.reduce((acc, word) => acc.replace(word, "").trim(), name);
}

View File

@ -1,12 +1,33 @@
<script lang="ts">
import { stockTicker, screenWidth, coolMode } from "$lib/store";
import ArrowLogo from "lucide-svelte/icons/move-up-right";
import { stockTicker, selectedTimePeriod } from "$lib/store";
import { page } from "$app/stores";
import { goto } from "$app/navigation";
export let data;
let displaySubSection = "income";
function updateQuery(query: string) {
// Create a new URL object based on the current URL
const url = new URL($page.url.href);
// Update or remove the "query" parameter
if (query) {
url.searchParams.set("query", query);
} else {
url.searchParams.delete("query");
}
// Use goto to navigate to the same pathname with updated query parameters
goto(url.pathname + url.search, { replaceState: true });
$selectedTimePeriod = query;
}
function changeSubSection(state) {
// Allow for ttm to be explicitly set.
if (state === "ttm") {
displaySubSection = state;
return;
}
const subSectionMap = {
income: "/financials/income",
"balance-sheet": "/financials/balance-sheet",
@ -16,16 +37,26 @@
if (state !== "income" && subSectionMap[state]) {
displaySubSection = state;
//goto(`/stocks/${$stockTicker}${subSectionMap[state]}`);
} else {
displaySubSection = state;
//goto(`/stocks/${$stockTicker}/financials`);
}
}
// Reactive block to check URL
$: {
// If query param "query" equals "ttm", mark ttm as active.
if ($page?.url?.searchParams?.get("query") === "ttm") {
$selectedTimePeriod = "ttm";
} else if ($page?.url?.searchParams?.get("query") === "annual") {
$selectedTimePeriod = "annual";
} else if ($page?.url?.searchParams?.get("query") === "quarterly") {
$selectedTimePeriod = "quarterly";
} else {
$selectedTimePeriod = "annual";
}
if ($page?.url?.pathname) {
const parts = $page?.url?.pathname.split("/");
const parts = $page.url.pathname.split("/");
const sectionMap = {
income: "income",
"balance-sheet": "balance-sheet",
@ -33,12 +64,12 @@
ratios: "ratios",
};
const foundSection = parts?.find((part) =>
Object?.values(sectionMap)?.includes(part),
const foundSection = parts.find((part) =>
Object.values(sectionMap).includes(part),
);
displaySubSection =
Object?.keys(sectionMap)?.find(
Object.keys(sectionMap).find(
(key) => sectionMap[key] === foundSection,
) || "income";
}
@ -54,7 +85,7 @@
<main class="w-full">
<div class="m-auto">
<nav
class="mb-5 sm:mb-0 sm:ml-4 pt-1 text-sm sm:text-[1rem] whitespace-nowrap overflow-x-auto whitespace-nowrap"
class="mb-5 sm:mb-0 sm:ml-4 pt-1 text-sm sm:text-[1rem] whitespace-nowrap overflow-x-auto"
>
<ul class="flex flex-row items-center w-full">
<a
@ -68,7 +99,9 @@
</a>
<a
href={`/stocks/${$stockTicker}/financials/balance-sheet`}
href={$selectedTimePeriod
? `/stocks/${$stockTicker}/financials/balance-sheet/?query=${$selectedTimePeriod}`
: "/stocks/${$stockTicker}/financials/balance-sheet"}
on:click={() => changeSubSection("balance-sheet")}
class="p-2 px-5 cursor-pointer {displaySubSection ===
'balance-sheet'
@ -78,7 +111,9 @@
Balance Sheet
</a>
<a
href={`/stocks/${$stockTicker}/financials/cash-flow`}
href={$selectedTimePeriod
? `/stocks/${$stockTicker}/financials/cash-flow/?query=${$selectedTimePeriod}`
: "/stocks/${$stockTicker}/financials/cash-flow"}
on:click={() => changeSubSection("cash-flow")}
class="p-2 px-5 cursor-pointer {displaySubSection ===
'cash-flow'
@ -96,6 +131,69 @@
>
Ratios
</a>
<a
href={$page?.url?.pathname}
on:click|preventDefault={() => updateQuery("annual")}
class="hidden sm:block ml-auto p-2 px-5 cursor-pointer {$selectedTimePeriod ===
'annual'
? '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'}"
>
Annual
</a>
<a
href={$page?.url?.pathname + "?query=quarterly"}
on:click|preventDefault={() => updateQuery("quarterly")}
class="hidden sm:block p-2 px-5 cursor-pointer {$selectedTimePeriod ===
'quarterly'
? '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'}"
>
Quarterly
</a>
<a
href={$page?.url?.pathname + "?query=ttm"}
on:click|preventDefault={() => updateQuery("ttm")}
class="hidden sm:block p-2 px-5 cursor-pointer {$selectedTimePeriod ===
'ttm'
? '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>
</ul>
<ul class="flex flex-row items-center w-full mt-1 sm:hidden">
<a
href={$page?.url?.pathname}
on:click|preventDefault={() => updateQuery("annual")}
class=" p-2 px-5 cursor-pointer {$selectedTimePeriod ===
'annual'
? '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'}"
>
Annual
</a>
<a
href={$page?.url?.pathname + "?query=quarterly"}
on:click|preventDefault={() => updateQuery("quarterly")}
class="p-2 px-5 cursor-pointer {$selectedTimePeriod ===
'quarterly'
? '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'}"
>
Quarterly
</a>
<a
href={$page?.url?.pathname + "?query=ttm"}
on:click|preventDefault={() => updateQuery("ttm")}
class="p-2 px-5 cursor-pointer {$selectedTimePeriod === 'ttm'
? '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>
</ul>
</nav>
</div>
@ -113,17 +211,14 @@
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
grid-auto-flow: column;
overflow-x: auto;
scrollbar-width: thin; /* Hide the default scrollbar in Firefox */
scrollbar-color: transparent transparent; /* Hide the default scrollbar in Firefox */
scrollbar-width: thin;
scrollbar-color: transparent transparent;
}
/* Custom scrollbar for Webkit (Chrome, Safari) */
.scrollbar::-webkit-scrollbar {
width: 0; /* Hide the width of the scrollbar */
height: 0; /* Hide the height of the scrollbar */
width: 0;
height: 0;
}
.scrollbar::-webkit-scrollbar-thumb {
background: transparent; /* Make the thumb transparent */
background: transparent;
}
</style>

View File

@ -292,65 +292,45 @@
<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 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">
{removeCompanyStrings($displayCompanyName)} Income Statement
</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="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"
>
{#each tabs as item, i}
{#if !["Pro", "Plus"]?.includes(data?.user?.tier) && i > 0}
<button
on:click={() => goto("/pricing")}
class="cursor-pointer group relative z-1 rounded-full w-1/2 min-w-24 md:w-auto px-5 py-1"
>
<span class="relative text-sm block font-semibold">
{item.title}
<svg
class="inline-block ml-0.5 -mt-1 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
>
</span>
</button>
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}
<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>
<span class="ml-2 text-sm"> Chart Mode </span>
{/if}
<span
class="relative text-sm block font-semibold whitespace-nowrap {activeIdx ===
i
? 'text-black'
: ''}"
>
{item.title}
</span>
</button>
{/if}
{/each}
</div>
</label>
</div>
<div class="grid grid-cols-1 gap-2">
{#if financialData?.length > 0}
<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"
>
<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">
<label
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
class="hidden sm:inline-flex ml-auto mt-2 sm:mt-0 cursor-pointer relative"
>
<input
on:click={toggleMode}
@ -368,110 +348,7 @@
<span class="ml-2 text-sm"> Chart Mode </span>
{/if}
</label>
<div class="flex flex-row items-center w-fit sm:ml-auto">
<!--
<div class="ml-2 relative inline-block text-left">
<Button
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"
>
<svg
class="w-4 h-4 bp:w-5 bp:h-5 pointer-events-none"
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.Trigger asChild let:builder>
<Button
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"
>
<span class="truncate">Raw</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.Group>
<DropdownMenu.Item
on:click={() => ($timeFrame = "5Y")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Billions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "10Y")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Millions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "MAX")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Raw
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
</div>
-->
</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}
{processedData}
color={["#ff00cc", "#37ff00", "#0c63e7", "#07c8f9"][
i % 4
]}
/>
{/each}
</div>
{: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">
<div class="ml-auto sm:ml-5 relative inline-block">
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild let:builder>
<Button
@ -526,6 +403,7 @@
</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"
@ -545,6 +423,22 @@
</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}
{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"
>
@ -559,7 +453,7 @@
{#each financialData as item}
{#if filterRule === "annual"}
<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}
</td>