update financial page by adding plot

This commit is contained in:
MuslemRahimi 2025-04-03 01:41:26 +02:00
parent 0139c5d4c5
commit 0a0c17573b
12 changed files with 796 additions and 179 deletions

View File

@ -97,25 +97,37 @@
},
xAxis: {
type: "datetime",
min: startTime,
max: endTime,
tickLength: 0,
min: startTime, // Force start at 9:30
max: endTime, // Force end at 16:10
crosshair: {
color: $mode === "light" ? "#545454" : "white",
width: 1,
color: $mode === "light" ? "black" : "white", // Set the color of the crosshair line
width: 1, // Adjust the line width as needed
dashStyle: "Solid",
},
labels: {
style: { color: $mode === "light" ? "#545454" : "white" },
distance: 20,
distance: 10, // Increases space between label and axis
formatter: function () {
const date = new Date(this?.value);
return date?.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
const timeString = date?.toLocaleTimeString("en-US", {
hour: "numeric",
hour12: true,
});
return `<span class="text-xs">${timeString.replace(/\s/g, " ")}</span>`;
},
},
tickPositioner: function () {
// Create custom tick positions with wider spacing
const positions = [];
const info = this.getExtremes();
const tickCount = 5; // Reduce number of ticks displayed
const interval = (info.max - info.min) / tickCount;
for (let i = 0; i <= tickCount; i++) {
positions.push(info.min + i * interval);
}
return positions;
},
},
yAxis: [
{
@ -203,6 +215,7 @@
],
},
color: "#4681f4",
borderColor: "4681f4",
lineWidth: 1.3,
yAxis: 0, // Use primary yAxis
animation: false,

View File

@ -1,9 +1,14 @@
<script lang="ts">
import { abbreviateNumber } from "$lib/utils";
import { screenWidth, stockTicker } from "$lib/store";
import { mode } from "mode-watcher";
import highcharts from "$lib/highcharts.ts";
export let data: any[];
export let fields: { label: string; key: string }[];
// Use a Set for fast margin lookup
let config = null;
const marginKeys = new Set([
/*
"pretaxProfitMargin",
@ -32,6 +37,117 @@
const formatted = abbreviateNumber(value.toFixed(2));
return isMargin ? formatted + "%" : formatted;
}
function plotData(label, key) {
const rawData = [...data]?.sort((a, b) => a?.fiscalYear - b?.fiscalYear);
const dateList = rawData?.map((item) =>
item?.period === "FY"
? item?.fiscalYear
: `${item?.period} FY ${item?.fiscalYear}`,
);
const valueList = rawData?.map((item) => item[key]);
const options = {
chart: {
type: "column",
backgroundColor: $mode === "light" ? "#fff" : "#09090B",
plotBackgroundColor: $mode === "light" ? "#fff" : "#09090B",
height: 360,
animation: false,
},
credits: { enabled: false },
legend: { enabled: false },
plotOptions: {
series: {
color: "white",
animation: false,
dataLabels: {
enabled: false,
color: "white",
style: {
fontSize: "13px",
fontWeight: "bold",
},
formatter: function () {
return abbreviateNumber(this?.y);
},
},
},
},
title: {
text: `<h3 class="mt-3 mb-1 sm:text-lg">${$stockTicker} ${label}</h3>`,
useHTML: true,
style: { color: $mode === "light" ? "black" : "white" },
},
xAxis: {
endOnTick: false,
categories: dateList,
crosshair: {
color: $mode === "light" ? "black" : "white", // Set the color of the crosshair line
width: 1, // Adjust the line width as needed
dashStyle: "Solid",
},
labels: {
style: {
color: $mode === "light" ? "#545454" : "white",
},
rotation: -45,
distance: 10, // Increases space between label and axis
},
},
yAxis: {
gridLineWidth: 1,
gridLineColor: $mode === "light" ? "#e5e7eb" : "#111827",
labels: {
style: { color: $mode === "light" ? "black" : "white" },
formatter: function () {
return abbreviateNumber(this.value);
},
},
title: { text: null },
opposite: true,
},
tooltip: {
shared: true,
useHTML: true,
backgroundColor: "rgba(0, 0, 0, 0.8)",
borderColor: "rgba(255, 255, 255, 0.2)",
borderWidth: 1,
style: {
color: "#fff",
fontSize: "16px",
padding: "10px",
},
borderRadius: 4,
formatter: function () {
let tooltipContent = `<span class="text-white m-auto text-black text-[1rem] font-[501]">${this?.x}</span><br>`;
this.points.forEach((point) => {
tooltipContent += `<span class="text-white font-semibold text-sm">${point.series.name}:</span>
<span class="text-white font-normal text-sm">${abbreviateNumber(
point.y,
)}</span><br>`;
});
return tooltipContent;
},
},
series: [
{
name: label,
data: valueList,
color: $mode === "light" ? "#2C6288" : "white",
borderColor: $mode === "light" ? "#2C6288" : "white",
borderRadius: "1px",
animation: false,
},
],
};
return options;
}
async function handleChart(label, key) {
config = plotData(label, key);
}
</script>
{#each computedFields as { label, key, isMargin } (key)}
@ -39,14 +155,69 @@
class="dark:sm:hover:bg-[#245073]/10 odd:bg-[#F6F7F8] dark:odd:bg-odd whitespace-nowrap"
>
<td
class="text-start border-r border-gray-300 dark:border-gray-700 text-sm sm:text-[1rem]"
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"
>
{label}
<span class="truncate w-fit max-w-40 sm:max-w-fit">{label}</span>
<label
for="financialPlotModal"
on:click={() => handleChart(label, key)}
class="cursor-pointer inline-block"
>
<svg
class="w-5 h-5 text-gray-500 dark:text-gray-300"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g
id="SVGRepo_tracerCarrier"
stroke-linecap="round"
stroke-linejoin="round"
></g><g id="SVGRepo_iconCarrier">
<path
d="M9 12H4.6C4.03995 12 3.75992 12 3.54601 12.109C3.35785 12.2049 3.20487 12.3578 3.10899 12.546C3 12.7599 3 13.0399 3 13.6V19.4C3 19.9601 3 20.2401 3.10899 20.454C3.20487 20.6422 3.35785 20.7951 3.54601 20.891C3.75992 21 4.03995 21 4.6 21H9M9 21H15M9 21L9 8.6C9 8.03995 9 7.75992 9.10899 7.54601C9.20487 7.35785 9.35785 7.20487 9.54601 7.10899C9.75992 7 10.0399 7 10.6 7H13.4C13.9601 7 14.2401 7 14.454 7.10899C14.6422 7.20487 14.7951 7.35785 14.891 7.54601C15 7.75992 15 8.03995 15 8.6V21M15 21H19.4C19.9601 21 20.2401 21 20.454 20.891C20.6422 20.7951 20.7951 20.6422 20.891 20.454C21 20.2401 21 19.9601 21 19.4V4.6C21 4.03995 21 3.75992 20.891 3.54601C20.7951 3.35785 20.6422 3.20487 20.454 3.10899C20.2401 3 19.9601 3 19.4 3H16.6C16.0399 3 15.7599 3 15.546 3.10899C15.3578 3.20487 15.2049 3.35785 15.109 3.54601C15 3.75992 15 4.03995 15 4.6V8"
stroke="currentColor"
stroke-width="1.8"
stroke-linecap="round"
stroke-linejoin="round"
></path>
</g></svg
>
</label>
</td>
{#each data as item, index (index)}
{#each data as item}
<td class="text-sm sm:text-[1rem] text-end">
{formatValue(item[key], isMargin)}
</td>
{/each}
</tr>
{/each}
<input type="checkbox" id="financialPlotModal" class="modal-toggle" />
<dialog
id="financialPlotModal"
class="modal {$screenWidth < 640 ? 'modal-bottom ' : ''} bg-[#000]/40 sm:px-5"
>
<label for="financialPlotModal" class="cursor-pointer modal-backdrop"></label>
<div
class="modal-box bg-white relative dark:bg-default w-full max-w-3xl rounded-md border-t sm:border border-gray-300 dark:border-gray-700 min-h-48 h-auto"
>
<label
for="financialPlotModal"
class="cursor-pointer text-[1.8rem] focus:outline-hidden absolute top-2 right-3"
>
<svg
class="w-8 h-8 text-muted dark:text-white"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="currentColor"
d="m6.4 18.308l-.708-.708l5.6-5.6l-5.6-5.6l.708-.708l5.6 5.6l5.6-5.6l.708.708l-5.6 5.6l5.6 5.6l-.708.708l-5.6-5.6z"
/>
</svg>
</label>
{#if config}
<div class="mt-2" use:highcharts={config}></div>
{/if}
</div>
</dialog>

View File

@ -7,7 +7,7 @@
export let data;
export let form;
let mode = false;
let mode = true;
const emailAddress = "support@stocknear.com";
function toggleMode() {

View File

@ -0,0 +1,32 @@
export const load = async ({ locals, params }) => {
const { apiKey, apiURL } = locals;
const postData = {
ticker: params.tickerID,
};
const getProfileData = async () => {
// make the POST request to the endpoint
const response = await fetch(apiURL + "/profile", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getProfileData: await getProfileData(),
};
};

View File

@ -2,15 +2,15 @@ import { error, fail, redirect } from "@sveltejs/kit";
import { validateData } from "$lib/utils";
import { loginUserSchema, registerUserSchema } from "$lib/schemas";
export const load = async ({ locals, params }) => {
const getData = async () => {
const { apiKey, apiURL } = locals;
const { apiKey, apiURL } = locals;
const postData = {
ticker: params.tickerID,
};
const getData = async () => {
// make the POST request to the endpoint
const response = await fetch(apiURL + "/stock-income", {
method: "POST",
@ -26,6 +26,8 @@ export const load = async ({ locals, params }) => {
return output;
};
// Make sure to return a promise
return {
getData: await getData(),

View File

@ -392,7 +392,7 @@
<div class="grid grid-cols-1 gap-2">
{#if financialData?.length > 0}
<div
class="mb-2 flex flex-row items-center w-full justify-end sm:justify-center"
class="flex flex-row items-center w-full justify-end sm:justify-center"
>
<label
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
@ -415,14 +415,35 @@
</label>
<div class="flex flex-row items-center w-fit sm:ml-auto">
<div class="relative inline-block text-left grow">
<!--
<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-2 rounded-md truncate"
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">{$timeFrame}</span>
<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"
@ -441,52 +462,30 @@
<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
Billions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "10Y")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
10 years
Millions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "MAX")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Max
Raw
</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-2 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>
@ -506,6 +505,91 @@
{/each}
</div>
{:else}
<div
class="flex flex-col sm:flex-row items-start sm:items-center sm:justify-between"
>
<span
class="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
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
>
@ -513,25 +597,40 @@
class="table table-sm table-compact no-scrollbar rounded-none sm:rounded-md w-full bg-white dark:bg-table border border-gray-300 dark:border-gray-800 m-auto"
>
<thead class="text-muted dark:text-white dark:bg-default">
<tr class="">
<tr class="border min-w-[250px]">
<td class="text-start text-sm font-semibold pr-10"
>Year</td
>Fiscal Year</td
>
{#each financialData as cash}
{#each financialData as item}
{#if filterRule === "annual"}
<td class=" font-semibold text-sm text-end">
{"FY" + cash?.fiscalYear?.slice(-2)}
<td
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
>
{"FY" + " " + item?.fiscalYear}
</td>
{:else}
<td class=" font-semibold text-sm text-end">
{"FY" +
cash?.fiscalYear?.slice(-2) +
" " +
cash?.period}
{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 -->

View File

@ -422,7 +422,7 @@
<div class="grid grid-cols-1 gap-2">
{#if financialData?.length > 0}
<div
class="mb-2 flex flex-row items-center w-full justify-end sm:justify-center"
class="flex flex-row items-center w-full justify-end sm:justify-center"
>
<label
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
@ -445,14 +445,35 @@
</label>
<div class="flex flex-row items-center w-fit sm:ml-auto">
<div class="relative inline-block text-left grow">
<!--
<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-2 rounded-md truncate"
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">{$timeFrame}</span>
<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"
@ -471,52 +492,30 @@
<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
Billions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "10Y")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
10 years
Millions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "MAX")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Max
Raw
</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-2 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>
@ -536,6 +535,91 @@
{/each}
</div>
{:else}
<div
class="flex flex-col sm:flex-row items-start sm:items-center sm:justify-between"
>
<span
class="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
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
>
@ -543,25 +627,40 @@
class="table table-sm table-compact no-scrollbar rounded-none sm:rounded-md w-full bg-white dark:bg-table border border-gray-300 dark:border-gray-800 m-auto"
>
<thead class="text-muted dark:text-white dark:bg-default">
<tr class="">
<tr class="border min-w-[250px]">
<td class="text-start text-sm font-semibold pr-10"
>Year</td
>Fiscal Year</td
>
{#each financialData as cash}
{#each financialData as item}
{#if filterRule === "annual"}
<td class=" font-semibold text-sm text-end">
{"FY" + cash?.fiscalYear?.slice(-2)}
<td
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
>
{"FY" + " " + item?.fiscalYear}
</td>
{:else}
<td class=" font-semibold text-sm text-end">
{"FY" +
cash?.fiscalYear?.slice(-2) +
" " +
cash?.period}
{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 -->

View File

@ -384,7 +384,7 @@
<div class="grid grid-cols-1 gap-2">
{#if financialData?.length > 0}
<div
class="mb-2 flex flex-row items-center w-full justify-end sm:justify-center"
class="flex flex-row items-center w-full justify-end sm:justify-center"
>
<label
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
@ -407,14 +407,35 @@
</label>
<div class="flex flex-row items-center w-fit sm:ml-auto">
<div class="relative inline-block text-left grow">
<!--
<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-2 rounded-md truncate"
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">{$timeFrame}</span>
<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"
@ -433,52 +454,30 @@
<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
Billions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "10Y")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
10 years
Millions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "MAX")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Max
Raw
</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-2 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>
@ -498,6 +497,91 @@
{/each}
</div>
{:else}
<div
class="flex flex-col sm:flex-row items-start sm:items-center sm:justify-between"
>
<span
class="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
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
>
@ -505,25 +589,40 @@
class="table table-sm table-compact no-scrollbar rounded-none sm:rounded-md w-full bg-white dark:bg-table border border-gray-300 dark:border-gray-800 m-auto"
>
<thead class="text-muted dark:text-white dark:bg-default">
<tr class="">
<tr class="border min-w-[250px]">
<td class="text-start text-sm font-semibold pr-10"
>Year</td
>Fiscal Year</td
>
{#each financialData as cash}
{#each financialData as item}
{#if filterRule === "annual"}
<td class=" font-semibold text-sm text-end">
{"FY" + cash?.fiscalYear?.slice(-2)}
<td
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
>
{"FY" + " " + item?.fiscalYear}
</td>
{:else}
<td class=" font-semibold text-sm text-end">
{"FY" +
cash?.fiscalYear?.slice(-2) +
" " +
cash?.period}
{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 -->

View File

@ -306,8 +306,8 @@
</script>
<SEO
title={`${$displayCompanyName} (${$stockTicker}) Cash Flow Statement`}
description={`Detailed cash flow statements for ${$displayCompanyName} (${$stockTicker}), including operating cash flow, capex and free cash flow.`}
title={`${$displayCompanyName} (${$stockTicker}) Ratios Statement`}
description={`Financial ratios and valuation metrics for ${$displayCompanyName} (${$stockTicker}). Includes annual, quarterly and trailing numbers with history and charts.`}
/>
<section class=" w-full overflow-hidden h-full">
@ -373,7 +373,7 @@
<div class="grid grid-cols-1 gap-2">
{#if financialData?.length > 0}
<div
class="mb-2 flex flex-row items-center w-full justify-end sm:justify-center"
class="flex flex-row items-center w-full justify-end sm:justify-center"
>
<label
class="inline-flex mt-2 sm:mt-0 cursor-pointer relative mr-auto"
@ -396,14 +396,35 @@
</label>
<div class="flex flex-row items-center w-fit sm:ml-auto">
<div class="relative inline-block text-left grow">
<!--
<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-2 rounded-md truncate"
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">{$timeFrame}</span>
<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"
@ -422,52 +443,30 @@
<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
Billions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "10Y")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
10 years
Millions
</DropdownMenu.Item>
<DropdownMenu.Item
on:click={() => ($timeFrame = "MAX")}
class="cursor-pointer sm:hover:bg-gray-300 dark:sm:hover:bg-primary"
>
Max
Raw
</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-2 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>
@ -487,6 +486,91 @@
{/each}
</div>
{:else}
<div
class="flex flex-col sm:flex-row items-start sm:items-center sm:justify-between"
>
<span
class="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
class="w-full rounded-none sm:rounded-md m-auto overflow-x-auto no-scrollbar"
>
@ -494,25 +578,40 @@
class="table table-sm table-compact no-scrollbar rounded-none sm:rounded-md w-full bg-white dark:bg-table border border-gray-300 dark:border-gray-800 m-auto"
>
<thead class="text-muted dark:text-white dark:bg-default">
<tr class="">
<tr class="border min-w-[250px]">
<td class="text-start text-sm font-semibold pr-10"
>Year</td
>Fiscal Year</td
>
{#each financialData as cash}
{#each financialData as item}
{#if filterRule === "annual"}
<td class=" font-semibold text-sm text-end">
{"FY" + cash?.fiscalYear?.slice(-2)}
<td
class=" font-semibold text-sm text-end border-l border-gray-300 dark:border-gray-800"
>
{"FY" + " " + item?.fiscalYear}
</td>
{:else}
<td class=" font-semibold text-sm text-end">
{"FY" +
cash?.fiscalYear?.slice(-2) +
" " +
cash?.period}
{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 -->

View File

@ -5,7 +5,6 @@ import { loginUserSchema, registerUserSchema } from "$lib/schemas";
export const load = async ({ params, locals }) => {
const getData = async () => {
let newsList;
const { apiURL, apiKey } = locals;
@ -23,9 +22,9 @@ export const load = async ({ params, locals }) => {
body: JSON.stringify(postData),
});
newsList = await response.json();
const output = await response.json();
return newsList;
return output;
};
// Make sure to return a promise

BIN
static/img/astronaut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 KiB

View File

@ -0,0 +1,4 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://www.canva.com/
HostUrl=https://export-download.canva.com/UUB-4/DAGjgDUUB-4/15/0/0001-8423318452819072229.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAQYCGKMUH5AO7UJ26%2F20250402%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250402T140745Z&X-Amz-Expires=17707&X-Amz-Signature=761feacdef49bdff3e2ad98387de9793c888669042037eefeb34735ca106cdbb&X-Amz-SignedHeaders=host&response-content-disposition=attachment%3B%20filename%2A%3DUTF-8%27%27Kopie%2520von%2520Kopie%2520von%2520Beige%2520Gr%25C3%25BCn%2520Neutral%2520Organisch%2520Pitch%2520Deck%2520Marketing%2520Pr%25C3%25A4sentation.png&response-expires=Wed%2C%2002%20Apr%202025%2019%3A02%3A52%20GMT