clean code metric page

This commit is contained in:
MuslemRahimi 2025-04-08 12:20:28 +02:00
parent b6d8342898
commit 97d3b168bb
3 changed files with 151 additions and 235 deletions

View File

@ -0,0 +1,106 @@
<script>
import { abbreviateNumber } from "$lib/utils";
export let title = "";
export let dateData = [];
export let names = [];
export let categoryValues = [];
export let growthValues = [];
export let getHref = (name) => "#"; // optional, for linking revenue names
// Helper to format dates consistently.
const formatDate = (d) => {
const date = new Date(d);
return isNaN(date)
? "Invalid Date"
: date.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
};
// Format a growth value for display.
const formatGrowth = (growthValue) => {
if (growthValue === null || growthValue === undefined) return "n/a";
return (growthValue > 0 ? "+" : "") + growthValue.toFixed(2) + "%";
};
</script>
<section class="my-10">
<h2 class="mt-5 text-xl sm:text-2xl font-bold mb-4">{title}</h2>
<div
class="no-scrollbar flex justify-start items-center w-screen sm:w-full mt-6 m-auto overflow-x-auto pr-5 sm:pr-0"
>
<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>
<th
class="border-b border-r border-gray-300 dark:border-gray-800 font-semibold text-sm text-start"
>
Period Ending
</th>
{#each dateData as item}
<th
class="z-20 border-b border-r min-w-[120px] border-gray-300 dark:border-gray-800 font-semibold text-sm text-end"
>
{formatDate(item)}
</th>
{/each}
</tr>
</thead>
<tbody class="shadow-md">
{#each names as name, index}
<tr class="odd:bg-[#F6F7F8] dark:odd:bg-odd">
<th
class="whitespace-nowrap text-sm sm:text-[1rem] font-normal text-start border-b border-r border-gray-300 dark:border-gray-800"
>
{#if title !== "Revenue by Geography" && getHref}
<a
href={getHref(name)}
class="sm:hover:text-blue-700 dark:sm:hover:text-blue-400 cursor-pointer underline underline-offset-4"
>
{name} Revenue
</a>
{:else}
{name} Revenue
{/if}
</th>
{#each categoryValues[index] as value}
<td
class="whitespace-nowrap text-sm sm:text-[1rem] text-end border-b border-r border-gray-300 dark:border-gray-800"
>
{@html value !== null && value !== undefined && value !== 0
? abbreviateNumber(value, false, true)
: "n/a"}
</td>
{/each}
</tr>
<tr>
<td
class="whitespace-nowrap text-sm sm:text-[1rem] font-normal text-start border-b border-r border-gray-300 dark:border-gray-800"
>
<span class="ml-2">{name} Revenue Growth</span>
</td>
{#each growthValues[index] as growthValue}
<td
class="text-sm sm:text-[1rem] text-end
{growthValue > 0
? 'text-green-800 dark:text-[#00FC50]'
: growthValue < 0
? 'text-red-800 dark:text-[#FF2F1F]'
: ''}
border-b border-r border-gray-300 dark:border-gray-800"
>
{formatGrowth(growthValue)}
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
</section>

View File

@ -1,8 +1,9 @@
<script lang="ts">
import { displayCompanyName, stockTicker } from "$lib/store";
import { abbreviateNumber } from "$lib/utils";
import { removeCompanyStrings } from "$lib/utils";
import Infobox from "$lib/components/Infobox.svelte";
import SEO from "$lib/components/SEO.svelte";
import MetricTable from "$lib/components/Table/MetricTable.svelte";
export let data;
@ -77,241 +78,43 @@
<div
class="relative flex justify-center items-center overflow-hidden w-full"
>
<div class="sm:pl-7 sm:pb-7 sm:pt-7 w-full m-auto mt-2 sm:mt-0">
<div class="sm:pl-7 sm:pb-7 w-full m-auto mt-2 sm:mt-0">
{#if revenueNames?.length !== 0 || geographicNames?.length !== 0}
<h2 class="mt-5 text-xl sm:text-2xl font-bold mb-4">
{$displayCompanyName} Revenue Breakdown
</h2>
<div
class="no-scrollbar flex justify-start items-center w-screen sm:w-full mt-6 m-auto overflow-x-auto pr-5 sm:pr-0"
>
<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>
<th
class=" border-b border-gray-300 dark:border-gray-800 font-semibold text-sm sm:text-[1rem] text-start"
>Quarter</th
>
{#each xData as item}
<th
class="z-20 border-b border-gray-300 dark:border-gray-800 font-semibold text-sm text-center"
>{new Date(item ?? null)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}</th
>
{/each}
</tr>
</thead>
<tbody class="shadow-md">
{#each revenueNames as name, index}
<tr class=" odd:bg-[#F6F7F8] dark:odd:bg-odd">
<th
class="whitespace-nowrap odd:bg-[#F6F7F8] dark:odd:bg-odd text-sm sm:text-[1rem] font-normal text-start border-b border-gray-300 dark:border-gray-800"
>
<a
href={getHref(name)}
class="sm:hover:text-blue-700 dark:sm:hover:text-blue-400 cursor-pointer underline underline-offset-4"
>
{name} Revenue
</a>
</th>
{#each categoryValues[index] as value}
<td
class=" text-sm sm:text-[1rem] text-end border-b border-gray-300 dark:border-gray-800"
>
{@html value !== null && value !== undefined
? abbreviateNumber(value, false, true)
: "n/a"}
</td>
{/each}
</tr>
<tr class="">
<td
class=" whitespace-nowrap text-sm sm:text-[1rem] text-start border-b border-gray-300 dark:border-gray-800"
>
<span class="ml-2">{name} Revenue Growth</span>
</td>
{#each growthValues[index] as growthValue}
<td
class="text-sm sm:text-[1rem] text-end {growthValue > 0
? 'text-green-800 dark:text-[#00FC50]'
: growthValue < 0
? 'text-red-800 dark:text-[#FF2F1F]'
: ''} border-b border-gray-300 dark:border-gray-800"
>
{growthValue > 0 ? "+" : ""}{growthValue !== null &&
growthValue !== undefined
? growthValue?.toFixed(2) + "%"
: "n/a"}
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
{#if geographicNames?.length !== 0}
<h2 class="mt-10 text-xl sm:text-2xl font-bold mb-4">
Revenue by Geography
</h2>
<div
class="no-scrollbar flex justify-start items-center w-screen sm:w-full mt-6 m-auto overflow-x-auto ovef pr-5 sm:pr-0"
>
<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>
<th
class=" border-b border-gray-300 dark:border-gray-800 font-semibold text-sm sm:text-[1rem] text-start"
>Quarter</th
>
{#each geographicXData as item}
<th
class="z-20 border-b border-gray-300 dark:border-gray-800 font-semibold text-sm text-center"
>{new Date(item ?? null)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}</th
>
{/each}
</tr>
</thead>
<tbody class="shadow-md">
{#each geographicNames as name, index}
<tr class=" odd:bg-[#F6F7F8] dark:odd:bg-odd">
<th
class="whitespace-nowrap dark:odd:bg-odd text-sm sm:text-[1rem] font-normal text-start border-b border-gray-300 dark:border-gray-800"
>{name} Revenue</th
>
{#each geographiCategoryValues[index] as value}
<td
class="whitespace-nowrap text-sm sm:text-[1rem] font-normal text-center border-b border-gray-300 dark:border-gray-800"
>
{@html value !== null &&
value !== 0 &&
value !== undefined
? abbreviateNumber(value, false, true)
: "n/a"}
</td>
{/each}
</tr>
<tr>
<td
class="whitespace-nowrap text-sm sm:text-[1rem] font-normal text-start border-b border-gray-300 dark:border-gray-800"
>
<span class="ml-2">{name} Revenue Growth</span>
</td>
{#each geographicGrowthValues[index] as growthValue}
<td
class="text-sm sm:text-[1rem] text-center {growthValue >
0
? 'text-green-800 dark:text-[#00FC50]'
: growthValue < 0
? 'text-red-800 dark:text-[#FF2F1F]'
: ''} border-b border-gray-300 dark:border-gray-800"
>
{growthValue > 0 ? "+" : ""}{growthValue !== null &&
growthValue !== 0 &&
growthValue !== undefined
? growthValue?.toFixed(2) + "%"
: "n/a"}
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
{#if revenueNames?.length}
<MetricTable
title="{removeCompanyStrings(
$displayCompanyName,
)} Revenue Breakdown"
dateData={xData}
names={revenueNames}
{categoryValues}
{growthValues}
{getHref}
/>
{/if}
{#if operatingExpensesNames?.length !== 0}
<h2 class="mt-10 text-xl sm:text-2xl font-bold mb-4">
Operating Expense Breakdown
</h2>
{#if geographicNames?.length}
<MetricTable
title="Revenue by Geography"
dateData={geographicXData}
names={geographicNames}
categoryValues={geographiCategoryValues}
growthValues={geographicGrowthValues}
/>
{/if}
<div
class="no-scrollbar flex justify-start items-center w-screen sm:w-full mt-6 m-auto overflow-x-auto pr-5 sm:pr-0"
>
<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>
<th
class=" border-b border-gray-300 dark:border-gray-800 font-semibold text-sm sm:text-[1rem] text-start"
>Quarter</th
>
{#each operatingExpensesXData as item}
<th
class="z-20 border-b border-gray-300 dark:border-gray-800 font-semibold text-sm text-center"
>{new Date(item ?? null)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}</th
>
{/each}
</tr>
</thead>
<tbody class="shadow-md">
{#each operatingExpensesNames as name, index}
<tr class=" odd:bg-[#F6F7F8] dark:odd:bg-odd">
<th
class="whitespace-nowrap odd:bg-[#F6F7F8] dark:odd:bg-odd text-sm sm:text-[1rem] font-normal text-start border-b border-gray-300 dark:border-gray-800"
>{name} Revenue</th
>
{#each operatingExpensesCategoryValues[index] as value}
<td
class=" whitespace-nowrap text-sm sm:text-[1rem] text-center border-b border-gray-300 dark:border-gray-800"
>
{@html value !== null &&
value !== 0 &&
value !== undefined
? abbreviateNumber(value, false, true)
: "n/a"}
</td>
{/each}
</tr>
<tr class="">
<td
class="whitespace-nowrap text-sm sm:text-[1rem] font-normal text-start border-b border-gray-300 dark:border-gray-800"
>
<span class="ml-2">{name} Revenue Growth</span>
</td>
{#each operatingExpensesGrowthValues[index] as growthValue}
<td
class="text-sm sm:text-[1rem] text-center {growthValue >
0
? 'text-green-800 dark:text-[#00FC50]'
: growthValue < 0
? 'text-red-800 dark:text-[#FF2F1F]'
: ''} border-b border-gray-300 dark:border-gray-800"
>
{growthValue > 0 ? "+" : ""}{growthValue !== null &&
growthValue !== 0 &&
growthValue !== undefined
? growthValue?.toFixed(2) + "%"
: "n/a"}
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
{#if operatingExpensesNames?.length}
<MetricTable
title="Operating Expense Breakdown"
dateData={operatingExpensesXData}
names={operatingExpensesNames}
categoryValues={operatingExpensesCategoryValues}
growthValues={operatingExpensesGrowthValues}
/>
{/if}
{:else}
<Infobox
text={`Currently, there are no business metrics available for ${$stockTicker}.`}
text={`Currently, there are no business metrics available for ${removeCompanyStrings($displayCompanyName)}.`}
/>
{/if}
</div>

View File

@ -38,7 +38,6 @@
}
function plotData() {
console.log(dataset);
const plotDataset = [...dataset]?.sort(
(a, b) => new Date(a?.date) - new Date(b?.date),
);
@ -125,7 +124,6 @@
// Loop through each point in the shared tooltip
this.points.forEach((point) => {
tooltipContent += `
<span style="display:inline-block; width:10px; height:10px; background-color:${point.color}; border-radius:50%; margin-right:5px;"></span>
<span class="font-semibold text-sm">${point.series.name}:</span>
<span class="font-normal text-sm">${abbreviateNumber(point.y)}</span><br>`;
});
@ -155,6 +153,7 @@
name: "Revenue",
data: valueList,
color: $mode === "light" ? "#2C6288" : "white",
borderRadius: "2",
},
],
legend: {
@ -221,10 +220,18 @@
>
<thead class="text-muted dark:text-white dark:bg-default">
<tr class="border-b border-gray-300 dark:border-gray-800">
<th class=" font-semibold text-start text-sm">Quarter</th>
<th class=" font-semibold text-end text-sm">Value</th>
<th class=" font-semibold text-end text-sm"> Change </th>
<th class=" font-semibold text-end text-sm">Growth</th>
<th class=" font-semibold text-start text-sm sm:text-[1rem]"
>Quarter</th
>
<th class=" font-semibold text-end text-sm sm:text-[1rem]"
>Value</th
>
<th class=" font-semibold text-end text-sm sm:text-[1rem]">
Change
</th>
<th class=" font-semibold text-end text-sm sm:text-[1rem]"
>Growth</th
>
</tr>
</thead>
<tbody>