update ipo page

This commit is contained in:
MuslemRahimi 2025-01-18 16:04:08 +01:00
parent 815fb7c821
commit 5ed0dc996e
9 changed files with 596 additions and 401 deletions

View File

@ -881,8 +881,16 @@
{:else}
{item[column.key]}
{/if}
{:else if column?.type === "date"}
{new Date(item[column.key]).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
{:else if column?.type === "int"}
{@html abbreviateNumber(item[column.key], false, true)}
{@html column.key === "marketCap" && item[column.key] === 0
? "n/a"
: abbreviateNumber(item[column.key], false, true)}
{:else if column?.type === "decimal"}
{item[column.key]?.toLocaleString("en-US")}
{:else if column?.type === "decimalSign"}

View File

@ -18,8 +18,31 @@ export const load = async ({ locals }) => {
};
const getIPOCalendar = async () => {
// make the POST request to the endpoint
const postData = { year: "all" };
const response = await fetch(apiURL + "/ipo-calendar", {
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 {
getNews: await getNews(),
getIPOCalendar: await getIPOCalendar(),
};
};

View File

@ -1,18 +1,14 @@
<script lang="ts">
import { page } from "$app/stores";
import { numberOfUnreadNotification } from "$lib/store";
import ArrowLogo from "lucide-svelte/icons/move-up-right";
import { goto } from "$app/navigation";
import SEO from "$lib/components/SEO.svelte";
import ScrollToTop from "$lib/components/ScrollToTop.svelte";
export let data;
let navigation = [];
let displaySection = "2025";
let ipoNews = data?.getNews?.slice(0, 10);
let displaySection = "Latest";
for (let year = 2025; year >= 2019; year--) {
navigation?.push({ title: year, link: `/ipos/${year}` });
navigation.push({ title: year, link: `/ipos/${year}` });
}
$: {
@ -30,25 +26,38 @@
displaySection =
sectionMap[
parts?.find((part) => Object?.keys(sectionMap)?.includes(part))
] || "overview";
] || "Latest";
}
}
const tabs = [
{
title: "Recent",
path: "/ipos",
},
{
title: "IPO News",
path: "/ipos/news",
},
];
let activeIdx = 0;
// Subscribe to the $page store to reactively update the activeIdx based on the URL
$: if ($page.url.pathname === "/ipos") {
activeIdx = 0;
} else if ($page.url.pathname.startsWith("/ipos/news")) {
activeIdx = 1;
}
</script>
<!-- HEADER FOR BETTER SEO -->
<SEO
title="IPOs
Calendar"
description="A list of upcoming ipos on the US stock market."
/>
<section
class="w-full max-w-3xl sm:max-w-[1400px] overflow-hidden min-h-screen pt-5 px-4 lg:px-3 mb-20"
class="w-full max-w-3xl sm:max-w-[1400px] overflow-hidden pb-20 pt-5 px-4 lg:px-3"
>
<div class="text-sm breadcrumbs">
<div class="text-sm sm:text-[1rem] breadcrumbs">
<ul>
<li><a href="/" class="text-gray-300">Home</a></li>
<li class="text-gray-300">Recent IPOs</li>
<li class="text-gray-300">IPO Data</li>
</ul>
</div>
@ -57,90 +66,62 @@
<div
class="relative flex justify-center items-start overflow-hidden w-full"
>
<main class="w-full lg:w-3/4 lg:pr-10">
<div class="mb-5">
<h1 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Recent IPOs
</h1>
</div>
<main class="w-full lg:pr-5">
<h1 class="mb-6 text-white text-2xl sm:text-3xl font-bold">
{activeIdx === 0 ? "Recent IPOs" : "IPO News"}
</h1>
<div
class="w-full {$page?.url?.pathname === '/ipos'
? 'hidden'
: ''} mb-2"
>
<nav class="border-b-[2px] overflow-x-scroll whitespace-nowrap">
<ul
class="flex flex-row items-center w-full text-[1rem] sm:text-lg text-white"
>
<nav class=" border-b-[2px] overflow-x-scroll whitespace-nowrap">
<ul class="flex flex-row items-center w-full text-lg text-white">
{#each tabs as item, i}
<a
href={`/ipos`}
on:click={() => (displaySection = "recent")}
class="p-2 px-5 cursor-pointer {displaySection === 'recent'
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
href={item?.path}
class="p-2 px-5 cursor-pointer {activeIdx === i
? 'text-white bg-primary sm:hover:bg-opacity-[0.95] font-semibold'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
>
Recent
{item.title}
</a>
<a
href={`/ipos/news`}
on:click={() => (displaySection = "news")}
class="p-2 px-5 cursor-pointer {displaySection === 'news'
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
>
News
</a>
</ul>
</nav>
{/each}
</ul>
</nav>
{#if activeIdx === 0}
<nav class=" overflow-x-scroll whitespace-nowrap">
<ul
class="flex flex-row items-center w-full text-[1rem] text-white"
>
{#each ["2025", "2024", "2023", "2022", "2021", "2020", "2019"] as item}
<a
href={`/ipos/${item}`}
on:click={() => (displaySection = item)}
class="p-2 px-5 cursor-pointer {displaySection === item
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
>
{item}
</a>
{#each ["Latest", "2025", "2024", "2023", "2022", "2021", "2020", "2019"] as item}
{#if item !== "Latest"}
<a
href={`/ipos/${item}`}
on:click={() => (displaySection = item)}
class="p-2 px-5 cursor-pointer {displaySection === item
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
>
{item}
</a>
{:else}
<a
href={`/ipos`}
on:click={() => (displaySection = item)}
class="p-2 px-5 cursor-pointer {displaySection === item
? 'text-white bg-primary sm:hover:bg-opacity-[0.95]'
: 'text-gray-400 sm:hover:text-white sm:hover:bg-primary sm:hover:bg-opacity-[0.95]'}"
>
{item}
</a>
{/if}
{/each}
</ul>
</nav>
</div>
{/if}
<slot />
</main>
<aside class="hidden lg:block relative fixed w-1/4">
{#if ipoNews?.length !== 0}
<div
class="w-full sm:hover:text-white text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit"
>
<div class="p-4 text-sm">
<h3 class="text-xl text-white font-bold mb-3">IPO News</h3>
<ul class="text-white">
{#each ipoNews?.slice(0, 10) as item}
<li class="mb-3 last:mb-1">
{item?.timestamp}
<br />
<a
class="sm:hover:text-white text-blue-400"
href={item?.link}
target="_blank"
rel="noopener noreferrer nofollow">{item?.title}</a
>
</li>
{/each}
</ul>
</div>
</div>
{/if}
</aside>
<ScrollToTop />
</main>
</div>
</div>
</div>

View File

@ -1,139 +1,91 @@
<script lang="ts">
import { formatString, abbreviateNumber } from "$lib/utils";
import { screenWidth } from "$lib/store";
import { onMount } from "svelte";
import SEO from "$lib/components/SEO.svelte";
import Table from "$lib/components/Table/Table.svelte";
export let data;
let rawData = data?.getIPOCalendar;
let ipoList = rawData?.slice(0, 150);
let ipoNews = data?.getNews;
let rawData = data?.getIPOCalendar?.slice(0, 200) ?? [];
const excludedRules = new Set([
"volume",
"price",
"ipoPrice",
"return",
"changesPercentage",
"eps",
"ipoDate",
"marketCap",
]);
let isLoaded = false;
const defaultList = [
{ name: "IPO Date", rule: "ipoDate" },
{ name: "IPO Price", rule: "ipoPrice" },
{ name: "Current Price", rule: "currentPrice" },
{ name: "Return Since", rule: "return" },
];
async function handleScroll() {
const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height
const isBottom = window.innerHeight + window.scrollY >= scrollThreshold;
if (isBottom && ipoList?.length !== rawData?.length) {
const nextIndex = ipoList?.length;
const filteredNewResults = rawData?.slice(nextIndex, nextIndex + 50);
ipoList = [...ipoList, ...filteredNewResults];
}
}
onMount(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
});
let charNumber = 40;
$: {
if ($screenWidth < 640) {
charNumber = 20;
} else {
charNumber = 40;
}
}
const specificRows = [
{ name: "Return Since", rule: "return", type: "percentSign" },
{ name: "IPO Date", rule: "ipoDate", type: "date" },
{ name: "IPO Price", rule: "ipoPrice", type: "float" },
{ name: "Current Price", rule: "currentPrice", type: "float" },
];
</script>
<section class="w-full overflow-hidden m-auto">
<SEO
title="200 Most Recent IPOs"
description="Detailed information the last 200 IPOs (initial public offerings) on the stock market. Includes IPO prices, dates, total returns and more."
/>
<div class="w-full overflow-x-scroll">
<table
class="mt-5 table table-sm table-compact rounded-none sm:rounded-md w-full bg-table border border-gray-800 m-auto overflow-hidden"
>
<thead>
<tr>
<th class="text-white font-medium text-[1rem] text-start"
>IPO Date</th
>
<th class="text-white font-medium text-[1rem] text-start"
>Symbol</th
>
<th class="text-white font-medium text-[1rem]">Name</th>
<th class="text-white font-medium text-end text-[1rem]"
>IPO Price</th
>
<th class="text-white font-medium text-end text-[1rem]"
>Current Price</th
>
<th class="text-white font-medium text-end text-[1rem]"
>Return Since</th
>
</tr>
</thead>
<tbody>
{#each ipoList as item}
<tr
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border-b-[#09090B]"
>
<td
class="text-white text-sm sm:text-[1rem] text-start border-b-[#09090B] whitespace-nowrap"
>
{new Date(item?.date)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
daySuffix: "2-digit",
})}
</td>
<td
class="whitespace-nowrap text-sm sm:text-[1rem] text-start border-b-[#09090B]"
>
<a
href={"/stocks/" + item?.symbol}
class="sm:hover:text-white text-blue-400"
>
{item?.symbol}
</a>
</td>
<td
class="text-white whitespace-nowrap text-sm sm:text-[1rem] border-b-[#09090B]"
>
{item?.name?.length > charNumber
? formatString(item?.name?.slice(0, charNumber)) + "..."
: formatString(item?.name)}
</td>
<td
class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap"
>
{item?.ipoPrice !== null ? item?.ipoPrice : "-"}
</td>
<td
class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap"
>
{item?.currentPrice !== null ? "$" + item?.currentPrice : "-"}
</td>
<td
class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap flex flex-row items-center justify-end"
>
{#if item?.return >= 0 && item?.return !== null}
<span class="inline-block text-[#00FC50] text-md"
>{abbreviateNumber(item?.return)}%</span
<div class="w-full overflow-hidden m-auto mt-5">
<h2 class="text-white text-xl font- text-start mt-5 w-full font-semibold">
Last {rawData?.length} IPOs
</h2>
<div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden">
<div
class="relative flex justify-center items-start overflow-hidden w-full"
>
<main class="w-full lg:w-3/4 lg:pr-10">
<div class="w-full overflow-x-scroll">
<Table
{data}
{rawData}
{excludedRules}
{defaultList}
{specificRows}
/>
</div>
</main>
<aside class="hidden lg:block relative fixed w-1/4">
{#if ipoNews?.length !== 0}
<div
class="w-full sm:hover:text-white text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit"
>
<div class="p-4 text-sm">
<h3 class="text-xl text-white font-bold mb-3">IPO News</h3>
<ul class="text-white">
{#each ipoNews?.slice(0, 10) as item}
<li class="mb-3 last:mb-1">
{item?.timestamp}
<a
class="sm:hover:text-white text-blue-400"
href={item?.link}
target="_blank"
rel="noopener noreferrer nofollow">{item?.title}</a
>
{:else if item?.return < 0 && item?.return !== null}
<span class="inline-block text-[#FF2F1F] text-md"
>{abbreviateNumber(item?.return)}%
</span>
{:else}
<span class="inline-block text-white text-md"> - </span>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</li>
{/each}
</ul>
<a
href={`/ipos/news`}
class="flex justify-center items-center rounded cursor-pointer w-full py-2 mt-3 text-[1rem] text-center font-semibold text-black m-auto sm:hover:bg-gray-300 bg-[#fff] transition duration-100"
>
More IPO News
</a>
</div>
</div>
{/if}
</aside>
</div>
</section>
</div>
</div>

View File

@ -1,22 +1,4 @@
export const load = async ({ locals, params }) => {
const getIPOCalendar = async () => {
const { apiURL, apiKey } = locals;
// make the POST request to the endpoint
const postData = { year: params.slug };
const response = await fetch(apiURL + "/ipo-calendar", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
const getYear = async () => {
return params.slug;
@ -24,7 +6,6 @@ export const load = async ({ locals, params }) => {
// Make sure to return a promise
return {
getIPOCalendar: await getIPOCalendar(),
getYear: await getYear(),
};
};

View File

@ -4,12 +4,17 @@
import { page } from "$app/stores";
import { onMount } from "svelte";
import Infobox from "$lib/components/Infobox.svelte";
import SEO from "$lib/components/SEO.svelte";
export let data;
let rawData = data?.getIPOCalendar;
let rawData = data?.getIPOCalendar?.filter(
(item) => new Date(item?.ipoDate).getFullYear() >= data?.getYear,
);
let ipoList = rawData?.slice(0, 150);
let year = data?.getYear;
let ipoNews = data?.getNews;
async function handleScroll() {
const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height
@ -30,124 +35,177 @@
$: {
if ($page?.url?.pathname) {
rawData = data?.getIPOCalendar;
rawData = data?.getIPOCalendar?.filter(
(item) => new Date(item?.ipoDate).getFullYear() == data?.getYear,
);
ipoList = rawData?.slice(0, 150);
year = data?.getYear;
}
}
$: charNumber = $screenWidth < 640 ? 20 : 40;
$: charNumber = $screenWidth < 640 ? 20 : 30;
</script>
<section class="w-full overflow-hidden m-auto">
<div class="flex flex-col justify-center items-center">
{#if rawData?.length !== 0}
<h2 class="text-white text-xl font- text-start mt-5 w-full font-semibold">
{rawData?.length} IPOs
</h2>
<div class="w-full overflow-x-scroll">
<table
class="mt-5 table table-sm table-compact rounded-none sm:rounded-md w-full bg-table border border-gray-800 m-auto overflow-hidden"
>
<thead class="bg-default">
<tr>
<th class="text-white font-semibold text-sm text-start"
>IPO Date</th
>
<th class="text-white font-semibold text-sm text-start">Symbol</th
>
<th class="text-white font-semibold text-sm">Name</th>
<th class="text-white font-semibold text-end text-sm"
>IPO Price</th
>
<th class="text-white font-semibold text-end text-sm"
>Current Price</th
>
<th class="text-white font-semibold text-end text-sm"
>Return Since</th
>
</tr>
</thead>
<tbody>
{#each ipoList as item}
<tr
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border-b-[#09090B]"
>
<td
class="text-white text-sm sm:text-[1rem] whitespace-nowrap text-start border-b-[#09090B] whitespace-nowrap"
>
{new Date(item?.date)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
daySuffix: "2-digit",
})}
</td>
<SEO
title={`All ${data?.getYear} IPOs - A Complete List`}
description={`A list of all the initial public offerings (IPOs) on the US stock market in the year ${data?.getYear}.`}
/>
<td
class="text-sm sm:text-[1rem] whitespace-nowrap text-start border-b-[#09090B] whitespace-nowrap"
>
<a
href={"/stocks/" + item?.symbol}
class="sm:hover:text-white text-blue-400"
>
{item?.symbol}
</a>
</td>
<td
class="text-gray-200 border-b-[#09090B] whitespace-nowrap text-sm sm:text-[1rem]"
>
<span class="text-white"
>{item?.name?.length > charNumber
? formatString(item?.name?.slice(0, charNumber)) + "..."
: formatString(item?.name)}</span
>
</td>
<td
class="text-white text-sm sm:text-[1rem] whitespace-nowrap border-b-[#09090B] text-end"
>
{item?.ipoPrice !== null ? item?.ipoPrice : "-"}
</td>
<td
class="text-white border-b-[#09090B] text-sm sm:text-[1rem] text-end"
>
{item?.currentPrice !== null ? item?.currentPrice : "-"}
</td>
<td
class="text-white border-b-[#09090B] text-end flex flex-row items-center justify-end"
>
{#if item?.return >= 0 && item?.return !== null}
<span
class="inline-block text-[#00FC50] text-sm sm:text-[1rem] whitespace-nowrap"
>+{abbreviateNumber(item?.return)}%</span
<div class="w-full overflow-hidden min-h-screen pb-20 m-auto mt-5">
<div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden">
<div
class="relative flex justify-center items-start overflow-hidden w-full"
>
<main class="w-full lg:w-3/4 lg:pr-10">
<div class="flex flex-col justify-center items-center">
{#if rawData?.length !== 0}
<h2
class="text-white text-xl font- text-start mt-5 w-full font-semibold"
>
{rawData?.length} IPOs
</h2>
<div class="w-full overflow-x-scroll">
<table
class="mt-5 table table-sm table-compact rounded-none sm:rounded-md w-full bg-table border border-gray-800 m-auto overflow-hidden"
>
<thead class="bg-default">
<tr>
<th class="text-white font-semibold text-sm text-start"
>IPO Date</th
>
{:else if item?.return < 0 && item?.return !== null}
<span
class="inline-block text-[#FF2F1F] text-sm sm:text-[1rem] whitespace-nowrap"
>{abbreviateNumber(item?.return)}%
</span>
{:else}
<span
class="inline-block text-white text-sm sm:text-[1rem] whitespace-nowrap"
<th class="text-white font-semibold text-sm text-start"
>Symbol</th
>
-
</span>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{:else}
<Infobox
text={`No IPOs found. Please adjust your search timeframe for the latest ${year}
<th class="text-white font-semibold text-sm">Name</th>
<th class="text-white font-semibold text-end text-sm"
>IPO Price</th
>
<th class="text-white font-semibold text-end text-sm"
>Current Price</th
>
<th class="text-white font-semibold text-end text-sm"
>Return Since</th
>
</tr>
</thead>
<tbody>
{#each ipoList as item}
<tr
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border-b-[#09090B]"
>
<td
class="text-white text-sm sm:text-[1rem] whitespace-nowrap text-start border-b-[#09090B] whitespace-nowrap"
>
{new Date(item?.ipoDate)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
daySuffix: "2-digit",
})}
</td>
<td
class="text-sm sm:text-[1rem] whitespace-nowrap text-start border-b-[#09090B] whitespace-nowrap"
>
<a
href={"/stocks/" + item?.symbol}
class="sm:hover:text-white text-blue-400"
>
{item?.symbol}
</a>
</td>
<td
class="text-gray-200 border-b-[#09090B] whitespace-nowrap text-sm sm:text-[1rem]"
>
<span class="text-white"
>{item?.name?.length > charNumber
? formatString(item?.name?.slice(0, charNumber)) +
"..."
: formatString(item?.name)}</span
>
</td>
<td
class="text-white text-sm sm:text-[1rem] whitespace-nowrap border-b-[#09090B] text-end"
>
{item?.ipoPrice !== null ? item?.ipoPrice : "n/a"}
</td>
<td
class="text-white border-b-[#09090B] text-sm sm:text-[1rem] text-end"
>
{item?.currentPrice !== null
? item?.currentPrice
: "n/a"}
</td>
<td
class="text-white border-b-[#09090B] text-end flex flex-row items-center justify-end"
>
{#if item?.return >= 0 && item?.return !== null}
<span
class="inline-block text-[#00FC50] text-sm sm:text-[1rem] whitespace-nowrap"
>+{abbreviateNumber(item?.return)}%</span
>
{:else if item?.return < 0 && item?.return !== null}
<span
class="inline-block text-[#FF2F1F] text-sm sm:text-[1rem] whitespace-nowrap"
>{abbreviateNumber(item?.return)}%
</span>
{:else}
<span
class="inline-block text-white text-sm sm:text-[1rem] whitespace-nowrap"
>
n/a
</span>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{:else}
<div class="w-full">
<Infobox
text={`No IPOs found. Please adjust your search timeframe for the latest ${year}
IPOs.`}
/>
{/if}
/>
</div>
{/if}
</div>
</main>
<aside class="hidden lg:block relative fixed w-1/4">
{#if ipoNews?.length !== 0}
<div
class="w-full sm:hover:text-white text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit"
>
<div class="p-4 text-sm">
<h3 class="text-xl text-white font-bold mb-3">IPO News</h3>
<ul class="text-white">
{#each ipoNews?.slice(0, 10) as item}
<li class="mb-3 last:mb-1">
{item?.timestamp}
<a
class="sm:hover:text-white text-blue-400"
href={item?.link}
target="_blank"
rel="noopener noreferrer nofollow">{item?.title}</a
>
</li>
{/each}
</ul>
<a
href={`/ipos/news`}
class="flex justify-center items-center rounded cursor-pointer w-full py-2 mt-3 text-[1rem] text-center font-semibold text-black m-auto sm:hover:bg-gray-300 bg-[#fff] transition duration-100"
>
More IPO News
</a>
</div>
</div>
{/if}
</aside>
</div>
</div>
</section>
</div>

View File

@ -1,26 +1,25 @@
export const load = async ({ locals }) => {
const getIPOCalendar = async () => {
const { apiURL, apiKey } = locals;
// make the POST request to the endpoint
const postData = { year: "all" };
const response = await fetch(apiURL + "/ipo-calendar", {
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 {
getIPOCalendar: await getIPOCalendar(),
};
};
export const load = async ({ locals }) => {
const { apiURL, apiKey } = locals;
const getStockNews = async () => {
const postData = { newsType: "stock-news" };
// make the POST request to the endpoint
const response = await fetch(apiURL + "/market-news", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
return {
getStockNews: await getStockNews(),
};
};

View File

@ -0,0 +1,218 @@
<script lang="ts">
import { onMount } from "svelte";
import SEO from "$lib/components/SEO.svelte";
export let data;
let rawData = data?.getNews;
let marketNews = data?.getStockNews;
let news = rawData.slice(0, 10) ?? [];
const formatDate = (dateString) => {
// Create a date object for the input dateString
const inputDate = new Date(dateString);
// Create a date object for the current time in New York City
const nycTime = new Date().toLocaleString("en-US", {
timeZone: "America/New_York",
});
const currentNYCDate = new Date(nycTime);
// Calculate the difference in milliseconds
const difference = inputDate.getTime() - currentNYCDate.getTime();
// Convert the difference to minutes
const minutes = Math.abs(Math.round(difference / (1000 * 60)));
if (minutes < 60) {
return `${minutes} minutes`;
} else if (minutes < 1440) {
const hours = Math.round(minutes / 60);
return `${hours} hour${hours !== 1 ? "s" : ""}`;
} else {
const days = Math.round(minutes / 1440);
return `${days} day${days !== 1 ? "s" : ""}`;
}
};
async function handleScroll() {
const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height
const isBottom = window.innerHeight + window.scrollY >= scrollThreshold;
if (isBottom && news?.length !== rawData?.length) {
const nextIndex = news?.length;
const filteredNewResults = rawData?.slice(nextIndex, nextIndex + 25);
news = [...news, ...filteredNewResults];
}
}
onMount(async () => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
});
function checkIfYoutubeVideo(link) {
const url = new URL(link);
if (url.hostname === "www.youtube.com") {
const searchParams = url.searchParams;
searchParams?.delete("t"); // Remove the "t" parameter
const videoIdMatch = url?.search?.match(/v=([^&]+)/);
if (videoIdMatch) {
return videoIdMatch[1];
}
} else {
return null;
}
}
// Track whether each video should be shown
let showVideo = {};
function handlePlayClick(index: number) {
showVideo = { ...showVideo, [index]: true };
}
</script>
<SEO
title="Latest IPO News"
description="The latest news about initial public offerings (IPOs) on the stock market, including both recent and upcoming IPOs."
/>
<div class="w-full overflow-hidden m-auto mt-5">
<div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden">
<div
class="relative flex justify-center items-start overflow-hidden w-full"
>
<main class="w-full lg:w-3/4 lg:pr-10">
<div class="w-full m-auto">
<div class="grid grid-cols-1 gap-y-3">
{#if news?.length !== 0}
{#each news as item, index}
<div class="w-full flex flex-col bg-default rounded-md m-auto">
{#if checkIfYoutubeVideo(item.link)}
{#if showVideo[index]}
<!-- Show the YouTube iframe when the user clicks play -->
<div class="w-full aspect-video mb-4">
<iframe
class="w-full h-full rounded-md border border-gray-800"
src={`https://www.youtube.com/embed/${checkIfYoutubeVideo(item.link)}`}
frameborder="0"
allow="clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
></iframe>
</div>
{:else}
<!-- Show the image placeholder with a play button -->
<div class="w-full aspect-video">
<div class="mb-3 sm:order-3 lg:pr-2">
<div
class="group relative block cursor-pointer bg-black bg-cover bg-[center_50%] object-contain after:block after:pb-[56.25%] after:content-[''] rounded-sm focus:outline-none focus:ring-2 focus:ring-blue-brand_light focus:ring-offset-2"
style="background-image: url({item?.img});"
tabindex="0"
on:click={() => handlePlayClick(index)}
>
<div
class="absolute left-[50%] top-[50%] z-10 h-[46px] w-[70px] -translate-x-1/2 -translate-y-1/2 rounded-lg bg-[#212121] opacity-80 transition-all before:absolute before:left-[50%] before:top-[50%] before:-translate-x-1/2 before:-translate-y-1/2 before:border-y-[11px] before:border-l-[19px] before:border-r-0 before:border-transparent before:border-l-white before:content-[''] group-hover:bg-[#ff0000] group-hover:opacity-100"
></div>
</div>
</div>
</div>
{/if}
<div class="mt-3 w-full">
<h3 class="text-sm text-white/80 truncate mb-2">
{item}
</h3>
<a
href={item?.link}
rel="noopener noreferrer"
target="_blank"
class="text-lg sm:text-xl font-bold text-white"
>
{item?.title}
<p class="text-white text-sm mt-2 font-normal">
{item?.description?.length > 200
? item?.description?.slice(0, 200) + "..."
: item?.description}
</p>
</a>
</div>
{:else}
<!-- Default news article display -->
<div class="w-full flex flex-col sm:flex-row">
<a
href={item?.link}
rel="noopener noreferrer"
target="_blank"
class="w-full sm:max-w-56 h-fit max-h-96 sm:mr-3 border border-gray-800 rounded-md"
>
<div class="flex-shrink-0 m-auto">
<img
src={item?.img}
class="h-auto w-full rounded-md"
alt="news image"
loading="lazy"
/>
</div>
</a>
<div class="mt-3 sm:mt-0 w-full">
<h3 class="text-sm text-white/80 truncate mb-2">
{item?.timestamp}
</h3>
<a
href={item?.url}
rel="noopener noreferrer"
target="_blank"
class="text-lg sm:text-xl font-bold text-white"
>
{item?.title}
<p class="text-white text-sm mt-2 font-normal">
{item?.description?.length > 200
? item?.description?.slice(0, 200) + "..."
: item?.description}
</p>
</a>
</div>
</div>
{/if}
</div>
<hr class="border-gray-600 w-full m-auto mt-5 mb-5" />
{/each}
{/if}
</div>
</div>
</main>
<aside class="hidden lg:block relative fixed w-1/4">
{#if marketNews?.length !== 0}
<div
class="w-full sm:hover:text-white text-white border border-gray-600 rounded-md h-fit pb-2 mt-4 cursor-pointer bg-inherit"
>
<div class="p-4 text-sm">
<h3 class="text-xl text-white font-bold mb-3">Stocks News</h3>
<ul class="text-white">
{#each marketNews?.slice(0, 10) as item}
<li class="mb-3 last:mb-1">
{formatDate(item?.publishedDate)} ago -
<a
class="sm:hover:text-white text-blue-400"
href={item?.url}
target="_blank"
rel="noopener noreferrer nofollow">{item?.title}</a
>
- {item?.site}
</li>
{/each}
</ul>
<a
href={`/market-news`}
class="flex justify-center items-center rounded cursor-pointer w-full py-2 mt-5 text-[1rem] text-center font-semibold text-black m-auto sm:hover:bg-gray-300 bg-[#fff] transition duration-100"
>
More Stocks News
</a>
</div>
</div>
{/if}
</aside>
</div>
</div>
</div>

View File

@ -1,6 +1,6 @@
<script lang="ts">
import { numberOfUnreadNotification } from "$lib/store";
import { onMount } from "svelte";
import SEO from "$lib/components/SEO.svelte";
export let data;
@ -74,36 +74,11 @@
}
</script>
<svelte:head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>
{$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} All
Stock News · Stocknear
</title>
<meta
name="description"
content={`The latest news on individual stocks on the US stock market, gathered from trusted finance and investing websites.`}
/>
<!-- Other meta tags -->
<meta property="og:title" content={`All Stock News · Stocknear`} />
<meta
property="og:description"
content={`The latest news on individual stocks on the US stock market, gathered from trusted finance and investing websites.`}
/>
<meta property="og:type" content="website" />
<!-- Add more Open Graph meta tags as needed -->
<!-- Twitter specific meta tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={`All Stock News · Stocknear`} />
<meta
name="twitter:description"
content={`The latest news on individual stocks on the US stock market, gathered from trusted finance and investing websites.`}
/>
<!-- Add more Twitter meta tags as needed -->
</svelte:head>
<SEO
title="All
Stock News"
description="The latest news on individual stocks on the US stock market, gathered from trusted finance and investing websites."
/>
<div class="w-full overflow-hidden m-auto mt-5">
<div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden">