clean code metric page
This commit is contained in:
parent
b6d8342898
commit
97d3b168bb
106
src/lib/components/Table/MetricTable.svelte
Normal file
106
src/lib/components/Table/MetricTable.svelte
Normal 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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user