ui fixes
This commit is contained in:
parent
d800bd9084
commit
f1e1ecc2ac
@ -7,8 +7,8 @@
|
||||
</script>
|
||||
|
||||
<SEO
|
||||
title="Data Disclaimer"
|
||||
description="All data shown on this website is for informational purposes only and should not be relied upon to make trading or investing decisions."
|
||||
title="Data Disclaimer | StockNear - Financial Data Sources & Accuracy"
|
||||
description="Comprehensive information about our financial data sources, accuracy standards, and usage guidelines for stock market data, analyst estimates, and financial metrics."
|
||||
/>
|
||||
|
||||
<section
|
||||
@ -36,11 +36,14 @@
|
||||
<div class="w-full bg-default m-auto">
|
||||
<div>
|
||||
<p class="text-white mb-5 text-[1rem] sm:text-lg">
|
||||
The data on this site is for informational purposes only and
|
||||
should not be used for trading or investment decisions.
|
||||
The financial data presented on Stocknear is provided for
|
||||
informational and educational purposes only. This information
|
||||
should not be construed as financial advice or used as the sole
|
||||
basis for trading or investment decisions.
|
||||
<br /><br />
|
||||
We strive for accuracy and quality. If you spot errors, please contact
|
||||
us via
|
||||
We maintain rigorous standards for data accuracy and quality assurance.
|
||||
If you identify any discrepancies or require clarification, please
|
||||
reach out to us via
|
||||
<a
|
||||
href={discordURL}
|
||||
target="_blank"
|
||||
@ -49,7 +52,7 @@
|
||||
>
|
||||
Discord
|
||||
</a>
|
||||
or email us at
|
||||
or contact our support team at
|
||||
<a
|
||||
href="mailto:{emailAddress}"
|
||||
class="text-blue-400 hover:underline"
|
||||
@ -59,86 +62,103 @@
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
|
||||
Stock Charts
|
||||
Market Data & Stock Charts
|
||||
</h2>
|
||||
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
|
||||
Intraday and historical stock chart data are provided by
|
||||
Our comprehensive market data and real-time stock charts are
|
||||
powered by
|
||||
<a
|
||||
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-blue-400 hover:underline"
|
||||
>
|
||||
FMP
|
||||
</a>.
|
||||
Financial Modeling Prep (FMP)
|
||||
</a>, a leading provider of financial data services. This
|
||||
includes intraday pricing, historical data, and technical
|
||||
indicators.
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
|
||||
Stocks Covered
|
||||
Market Coverage
|
||||
</h2>
|
||||
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
|
||||
Stocknear covers all stocks and ETFs listed on major US
|
||||
exchanges (NASDAQ, NYSE, NYSE American, NYSE Arca) and many OTC
|
||||
stocks. We also include international companies trading on US
|
||||
exchanges and aim to add global stock exchanges soon.
|
||||
Stocknear provides comprehensive coverage of securities listed
|
||||
on major U.S. exchanges, including NASDAQ, NYSE, NYSE American,
|
||||
and NYSE Arca. Our database encompasses stocks, ETFs, ADRs, and
|
||||
select OTC securities. We are actively expanding our coverage to
|
||||
include additional global exchanges and financial instruments.
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
|
||||
Historical Financials
|
||||
Financial Fundamentals
|
||||
</h2>
|
||||
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
|
||||
Fundamental financial data is sourced from
|
||||
Our fundamental financial data is sourced from
|
||||
<a
|
||||
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-blue-400 hover:underline"
|
||||
>
|
||||
FMP
|
||||
</a>, based on official SEC filings (10-K, 10-Q). Data is
|
||||
updated daily, but delays may occur if companies file late. All
|
||||
displayed data is official, audited, and SEC-submitted.
|
||||
Financial Modeling Prep
|
||||
</a>
|
||||
and is derived from official SEC filings (Forms 10-K, 10-Q, and 8-K).
|
||||
While our systems update this information daily, there may be inherent
|
||||
delays due to standard SEC filing windows and corporate reporting
|
||||
schedules. All financial data presented is based on official, audited
|
||||
company submissions to regulatory authorities.
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
|
||||
Price Targets and Analyst Estimates
|
||||
Analyst Coverage & Market Projections
|
||||
</h2>
|
||||
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
|
||||
Price targets and ratings are from Benzinga, sourced from Wall
|
||||
Street analysts. Revenue and EPS forecasts come from Benzinga
|
||||
and <a
|
||||
Our analyst coverage integrates data from multiple authoritative
|
||||
sources. Price targets and analyst ratings are provided by
|
||||
Benzinga, representing consensus views from leading Wall Street
|
||||
analysts. Revenue projections and EPS forecasts are aggregated
|
||||
from both Benzinga and
|
||||
<a
|
||||
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-blue-400 hover:underline"
|
||||
>
|
||||
FMP
|
||||
</a>. Analyst accuracy and performance metrics are also provided
|
||||
by Benzinga. Differences across sources may occur but are
|
||||
directionally consistent. Use this information cautiously as
|
||||
predictions are often inaccurate.
|
||||
Financial Modeling Prep
|
||||
</a>. While we strive to present the most accurate forecasts,
|
||||
users should note that market predictions are inherently
|
||||
uncertain and should be considered as part of a broader
|
||||
investment research process.
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
|
||||
ETF Holdings
|
||||
ETF Analytics
|
||||
</h2>
|
||||
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
|
||||
Most ETF holdings data is provided by <a
|
||||
Our ETF holdings data and analytics are provided through our
|
||||
partnership with
|
||||
<a
|
||||
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-blue-400 hover:underline"
|
||||
>
|
||||
FMP
|
||||
</a>.
|
||||
Financial Modeling Prep
|
||||
</a>, ensuring accurate and timely information on fund
|
||||
compositions, allocations, and performance metrics.
|
||||
</p>
|
||||
|
||||
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
|
||||
Options Data
|
||||
Options Market Data
|
||||
</h2>
|
||||
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
|
||||
Options flow data is sourced from Benzinga via OPRA. Individual
|
||||
ticker options data is from Intrinio, also sourced from OPRA.
|
||||
Our options market data infrastructure combines real-time
|
||||
options flow information from Benzinga and individual options
|
||||
chain data from Intrinio, both sourced through the Options Price
|
||||
Reporting Authority (OPRA). This provides comprehensive coverage
|
||||
of options market activity and pricing across all major
|
||||
exchanges.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -18,9 +18,11 @@
|
||||
|
||||
let filterList = [];
|
||||
let weekdayFiltered = [];
|
||||
let weekday; // Added declaration
|
||||
let weekday = []; // our unordered week data
|
||||
let syncWorker: Worker | undefined;
|
||||
let pagePathName = $page?.url?.pathname;
|
||||
let pagePathName: string = "";
|
||||
// reassign pagePathName reactively
|
||||
$: pagePathName = $page?.url?.pathname || "";
|
||||
|
||||
const maxWeeksChange = 6;
|
||||
const today = new Date();
|
||||
@ -31,21 +33,27 @@
|
||||
let sortMode = false;
|
||||
$: testList = [];
|
||||
|
||||
// Get calendar data from our load function
|
||||
$: economicCalendar = data?.getEconomicCalendar;
|
||||
// Calculate the week days
|
||||
$: daysOfWeek = getDaysOfWeek(currentWeek);
|
||||
// Format days for header labels
|
||||
$: formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d"));
|
||||
$: {
|
||||
if (!sortMode) {
|
||||
weekday = getWeekdayData(economicCalendar, daysOfWeek);
|
||||
rawData = weekday;
|
||||
}
|
||||
|
||||
// Reapply filters whenever weekday data changes
|
||||
if (filterList.length > 0 && syncWorker) {
|
||||
loadWorker();
|
||||
}
|
||||
// Recalculate weekday data when the economicCalendar or days change – but only when not sorting
|
||||
$: if (!sortMode) {
|
||||
weekday = getWeekdayData(economicCalendar, daysOfWeek);
|
||||
rawData = weekday;
|
||||
}
|
||||
|
||||
// Whenever filters are applied and the worker exists, trigger filtering
|
||||
$: if (filterList.length > 0 && syncWorker) {
|
||||
loadWorker();
|
||||
}
|
||||
|
||||
// Create a consolidated derived value for our header and table rendering
|
||||
$: displayWeekData = filterList.length === 0 ? weekday : weekdayFiltered;
|
||||
|
||||
const startBoundary = subWeeks(
|
||||
startOfWeek(today, { weekStartsOn: 1 }),
|
||||
maxWeeksChange,
|
||||
@ -54,13 +62,13 @@
|
||||
startOfWeek(today, { weekStartsOn: 1 }),
|
||||
maxWeeksChange,
|
||||
);
|
||||
|
||||
$: previousMax = currentWeek <= startBoundary;
|
||||
$: nextMax = currentWeek >= endBoundary;
|
||||
|
||||
let currentDate = new Date();
|
||||
let selectedWeekday = Math.min((currentDate.getDay() + 6) % 7, 4);
|
||||
|
||||
// Returns an array of weekdays (Monday - Friday) for a given week.
|
||||
function getDaysOfWeek(week) {
|
||||
const startDate = startOfWeek(week, { weekStartsOn: 1 });
|
||||
return Array.from({ length: 5 }, (_, i) => ({
|
||||
@ -69,23 +77,27 @@
|
||||
}));
|
||||
}
|
||||
|
||||
// Retrieves and sorts calendar data for each day.
|
||||
function getWeekdayData(calendar, days) {
|
||||
if (!calendar) return [];
|
||||
return days.map((day) => {
|
||||
const dayData = calendar.filter(
|
||||
(item) => item.date === format(day.date, "yyyy-MM-dd"),
|
||||
);
|
||||
return dayData?.sort(
|
||||
return dayData.sort(
|
||||
(a, b) =>
|
||||
new Date(`1970-01-01T${a.time}`) - new Date(`1970-01-01T${b.time}`),
|
||||
new Date(`1970-01-01T${a.time}`).getTime() -
|
||||
new Date(`1970-01-01T${b.time}`).getTime(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle messages from our filtering web worker.
|
||||
const handleMessage = (event) => {
|
||||
weekdayFiltered = event.data?.finalData?.output ?? [];
|
||||
};
|
||||
|
||||
// Tell the web worker to filter our data
|
||||
const loadWorker = async () => {
|
||||
syncWorker?.postMessage({ rawData, filterList });
|
||||
};
|
||||
@ -115,7 +127,6 @@
|
||||
state === "previous"
|
||||
? subWeeks(currentWeek, 1)
|
||||
: addWeeks(currentWeek, 1);
|
||||
|
||||
if (newWeek >= startBoundary && newWeek <= endBoundary) {
|
||||
currentWeek = newWeek;
|
||||
}
|
||||
@ -123,21 +134,21 @@
|
||||
|
||||
function saveRules() {
|
||||
try {
|
||||
// Save the version along with the rules
|
||||
localStorage?.setItem(pagePathName, JSON?.stringify(filterList));
|
||||
localStorage?.setItem(pagePathName, JSON.stringify(filterList));
|
||||
} catch (e) {
|
||||
console.log("Failed saving filterlist: ", e);
|
||||
console.error("Failed saving filterlist:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Notice we now initialize checkedItems just once instead of using a reactive assignment.
|
||||
let checkedItems: Set<any> = new Set();
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const savedRules = localStorage?.getItem(pagePathName);
|
||||
|
||||
if (savedRules) {
|
||||
filterList = JSON.parse(savedRules);
|
||||
}
|
||||
|
||||
checkedItems = new Set(filterList);
|
||||
|
||||
if (!syncWorker) {
|
||||
@ -146,29 +157,23 @@
|
||||
syncWorker.onmessage = handleMessage;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
function handleInput(event) {
|
||||
const searchQuery = event.target.value?.toLowerCase() || "";
|
||||
|
||||
// Update the global searchQuery (avoid shadowing) and debounce the filtering.
|
||||
function handleInput(event: InputEvent) {
|
||||
searchQuery = (event.target as HTMLInputElement)?.value.toLowerCase() || "";
|
||||
setTimeout(() => {
|
||||
testList = [];
|
||||
|
||||
if (searchQuery.length > 0) {
|
||||
const rawList = listOfRelevantCountries;
|
||||
testList =
|
||||
rawList?.filter((item) => {
|
||||
const index = item?.toLowerCase();
|
||||
return index?.startsWith(searchQuery);
|
||||
}) || [];
|
||||
testList = listOfRelevantCountries.filter((item) =>
|
||||
item.toLowerCase().startsWith(searchQuery),
|
||||
);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
|
||||
$: checkedItems = new Set();
|
||||
|
||||
async function handleChangeValue(value) {
|
||||
if (checkedItems.has(value)) {
|
||||
checkedItems.delete(value);
|
||||
@ -190,23 +195,20 @@
|
||||
function handleReset() {
|
||||
filterList = [];
|
||||
checkedItems = new Set();
|
||||
|
||||
economicCalendar = data?.getEconomicCalendar;
|
||||
daysOfWeek = getDaysOfWeek(currentWeek);
|
||||
formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d"));
|
||||
weekday = getWeekdayData(economicCalendar, daysOfWeek);
|
||||
rawData = weekday;
|
||||
|
||||
currentWeek = startOfWeek(today, { weekStartsOn: 1 });
|
||||
selectedWeekday = Math.min((currentDate.getDay() + 6) % 7, 4);
|
||||
|
||||
previousMax = currentWeek <= startBoundary;
|
||||
nextMax = currentWeek >= endBoundary;
|
||||
|
||||
saveRules();
|
||||
}
|
||||
|
||||
let columns = [
|
||||
// Static columns (do not change across renders)
|
||||
const columns = [
|
||||
{ key: "time", label: "Time", align: "left" },
|
||||
{ key: "country", label: "Country", align: "left" },
|
||||
{ key: "event", label: "Event", align: "left" },
|
||||
@ -228,86 +230,70 @@
|
||||
|
||||
const sortData = (key) => {
|
||||
sortMode = true;
|
||||
for (const k in sortOrders) {
|
||||
if (k !== key) {
|
||||
sortOrders[k].order = "none";
|
||||
}
|
||||
}
|
||||
Object.keys(sortOrders).forEach((k) => {
|
||||
if (k !== key) sortOrders[k].order = "none";
|
||||
});
|
||||
|
||||
// Cycle through 'none', 'asc', 'desc' for the clicked key
|
||||
// Cycle through "none", "asc", "desc"
|
||||
const orderCycle = ["none", "asc", "desc"];
|
||||
const currentOrderIndex = orderCycle.indexOf(
|
||||
sortOrders[key]?.order || "none",
|
||||
);
|
||||
sortOrders[key] = {
|
||||
...(sortOrders[key] || {}),
|
||||
...sortOrders[key],
|
||||
order: orderCycle[(currentOrderIndex + 1) % orderCycle.length],
|
||||
};
|
||||
const sortOrder = sortOrders[key]?.order;
|
||||
|
||||
// Reset to original data when 'none' and stop further sorting
|
||||
const sortOrder = sortOrders[key].order;
|
||||
if (sortOrder === "none") {
|
||||
sortMode = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Generic comparison function
|
||||
const compareValues = (a, b) => {
|
||||
const { type } = sortOrders[key];
|
||||
let valueA, valueB;
|
||||
|
||||
switch (type) {
|
||||
case "date":
|
||||
valueA = new Date(a[key]);
|
||||
valueB = new Date(b[key]);
|
||||
valueA = new Date(a[key]).getTime();
|
||||
valueB = new Date(b[key]).getTime();
|
||||
break;
|
||||
case "rating":
|
||||
case "string":
|
||||
// Retrieve values
|
||||
valueA = a[key];
|
||||
valueB = b[key];
|
||||
|
||||
// Handle null or undefined values, always placing them at the bottom
|
||||
if (valueA == null && valueB == null) {
|
||||
return 0; // Both are null/undefined, no need to change the order
|
||||
} else if (valueA == null) {
|
||||
return 1; // null goes to the bottom
|
||||
} else if (valueB == null) {
|
||||
return -1; // null goes to the bottom
|
||||
}
|
||||
|
||||
// Convert the values to uppercase for case-insensitive comparison
|
||||
valueA = valueA?.toUpperCase();
|
||||
valueB = valueB?.toUpperCase();
|
||||
|
||||
// Perform the sorting based on ascending or descending order
|
||||
valueA = a[key] ? a[key].toUpperCase() : "";
|
||||
valueB = b[key] ? b[key].toUpperCase() : "";
|
||||
return sortOrder === "asc"
|
||||
? valueA?.localeCompare(valueB)
|
||||
: valueB?.localeCompare(valueA);
|
||||
? valueA.localeCompare(valueB)
|
||||
: valueB.localeCompare(valueA);
|
||||
case "number":
|
||||
default:
|
||||
valueA = parseFloat(a[key]);
|
||||
valueB = parseFloat(b[key]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sortOrder === "asc") {
|
||||
return valueA < valueB ? -1 : valueA > valueB ? 1 : 0;
|
||||
} else {
|
||||
return valueA > valueB ? -1 : valueA < valueB ? 1 : 0;
|
||||
}
|
||||
return sortOrder === "asc"
|
||||
? valueA < valueB
|
||||
? -1
|
||||
: valueA > valueB
|
||||
? 1
|
||||
: 0
|
||||
: valueA > valueB
|
||||
? -1
|
||||
: valueA < valueB
|
||||
? 1
|
||||
: 0;
|
||||
};
|
||||
|
||||
// Sort and update the originalData and stockList
|
||||
weekday[selectedWeekday] = [...rawData[selectedWeekday]].sort(
|
||||
compareValues,
|
||||
);
|
||||
// Create a new array copy to trigger reactivity (instead of in-place mutation)
|
||||
weekday = [
|
||||
...weekday.slice(0, selectedWeekday),
|
||||
[...rawData[selectedWeekday]].sort(compareValues),
|
||||
...weekday.slice(selectedWeekday + 1),
|
||||
];
|
||||
};
|
||||
</script>
|
||||
|
||||
<SEO
|
||||
title="Worldwide
|
||||
Economic Calendar"
|
||||
title="Worldwide Economic Calendar"
|
||||
description="A list of upcoming economic events on the US stock market, with dates, times and estimation."
|
||||
/>
|
||||
|
||||
@ -337,11 +323,10 @@
|
||||
<div class="flex justify-center w-full m-auto h-full overflow-hidden">
|
||||
<!-- Content area -->
|
||||
<div class="relative flex flex-col flex-1 overflow-hidden">
|
||||
<!-- Cards -->
|
||||
<!-- Header Dates -->
|
||||
<div
|
||||
class=" w-full flex flex-row justify-center m-auto items-center"
|
||||
class="w-full flex flex-row justify-center m-auto items-center"
|
||||
>
|
||||
<!-- Start Columns -->
|
||||
<label
|
||||
on:click={() => changeWeek("previous")}
|
||||
class="{previousMax
|
||||
@ -352,13 +337,14 @@
|
||||
class="w-6 h-6 m-auto rotate-180"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
>
|
||||
<path
|
||||
fill="white"
|
||||
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
|
||||
/></svg
|
||||
>
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
{#each filterList?.length === 0 ? weekday : weekdayFiltered as day, index}
|
||||
{#each displayWeekData as day, index (formattedWeekday[index])}
|
||||
<div
|
||||
class="w-full text-white {index === selectedWeekday
|
||||
? ''
|
||||
@ -366,13 +352,13 @@
|
||||
>
|
||||
<label
|
||||
on:click={() => toggleDate(index)}
|
||||
class=" m-auto w-full cursor-pointer h-16 {index ===
|
||||
class="m-auto w-full cursor-pointer h-16 {index ===
|
||||
selectedWeekday
|
||||
? 'bg-white text-black font-semibold'
|
||||
: ''} rounded sm:rounded-none flex bg-default border border-gray-600 mb-3"
|
||||
>
|
||||
<div
|
||||
class=" flex flex-row justify-center items-center w-full"
|
||||
class="flex flex-row justify-center items-center w-full"
|
||||
>
|
||||
<label
|
||||
on:click={() => clickWeekday("previous", index)}
|
||||
@ -382,18 +368,19 @@
|
||||
class="w-8 h-8 inline-block rotate-180"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
>
|
||||
<path
|
||||
fill="black"
|
||||
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
|
||||
/></svg
|
||||
>
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
<div
|
||||
class="flex flex-col items-center truncate m-auto p-1"
|
||||
>
|
||||
<span class="text-md">{formattedWeekday[index]}</span>
|
||||
<span class="text-[1rem] sm:text-sm m-auto pt-1 pb-1">
|
||||
{day?.length} Events</span
|
||||
<span class="text-[1rem] sm:text-sm m-auto pt-1 pb-1"
|
||||
>{day?.length} Events</span
|
||||
>
|
||||
</div>
|
||||
<label
|
||||
@ -404,11 +391,12 @@
|
||||
class="w-8 h-8 inline-block"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
>
|
||||
<path
|
||||
fill="black"
|
||||
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
|
||||
/></svg
|
||||
>
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
@ -424,14 +412,16 @@
|
||||
class="w-6 h-6 m-auto"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path
|
||||
>
|
||||
<path
|
||||
fill="white"
|
||||
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
|
||||
/></svg
|
||||
>
|
||||
/>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Dropdown Filters -->
|
||||
<div
|
||||
class="flex flex-row items-center w-full sm:w-fit m-auto sm:m-0 pt-6 pb-3"
|
||||
>
|
||||
@ -467,32 +457,28 @@
|
||||
class="relative sticky z-40 focus:outline-none -top-1"
|
||||
tabindex="0"
|
||||
role="menu"
|
||||
style=""
|
||||
>
|
||||
<input
|
||||
bind:value={searchQuery}
|
||||
on:input={handleInput}
|
||||
autocomplete="off"
|
||||
class=" absolute fixed sticky w-full border-0 bg-default border-b border-gray-200
|
||||
focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300"
|
||||
class="absolute sticky w-full border-0 bg-default border-b border-gray-200 focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300"
|
||||
type="search"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
<DropdownMenu.Group>
|
||||
{#each testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : listOfRelevantCountries as item}
|
||||
{#each searchQuery.length > 0 ? testList : listOfRelevantCountries as item}
|
||||
<DropdownMenu.Item class="sm:hover:bg-primary">
|
||||
<div class="flex items-center">
|
||||
<label
|
||||
on:click={() => {
|
||||
handleChangeValue(item);
|
||||
}}
|
||||
on:click={() => handleChangeValue(item)}
|
||||
class="cursor-pointer text-white"
|
||||
for={item}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checkedItems?.has(item)}
|
||||
checked={checkedItems.has(item)}
|
||||
/>
|
||||
<span class="ml-2">{item}</span>
|
||||
</label>
|
||||
@ -507,7 +493,7 @@
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button
|
||||
builders={[builder]}
|
||||
class="border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate"
|
||||
class="border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate"
|
||||
>
|
||||
<span class="truncate text-white"
|
||||
>Filter Importance</span
|
||||
@ -534,26 +520,23 @@
|
||||
class="relative sticky z-40 focus:outline-none -top-1"
|
||||
tabindex="0"
|
||||
role="menu"
|
||||
style=""
|
||||
></div>
|
||||
<DropdownMenu.Group>
|
||||
{#each [1, 2, 3] as i}
|
||||
<DropdownMenu.Item class="sm:hover:bg-primary">
|
||||
<div class="flex items-center">
|
||||
<label
|
||||
on:click={() => {
|
||||
handleChangeValue(i);
|
||||
}}
|
||||
on:click={() => handleChangeValue(i)}
|
||||
class="flex flex-row items-center cursor-pointer text-white"
|
||||
for={i}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checkedItems?.has(i)}
|
||||
checked={checkedItems.has(i)}
|
||||
/>
|
||||
<div class="ml-2 flex flex-row items-center">
|
||||
{#if i > 0}
|
||||
{#each Array(i).fill() as _, index}
|
||||
{#each Array(i) as _}
|
||||
<svg
|
||||
class="w-4 h-4 text-[#FBCE3C]"
|
||||
aria-hidden="true"
|
||||
@ -566,19 +549,6 @@
|
||||
/>
|
||||
</svg>
|
||||
{/each}
|
||||
{#each Array(3 - i).fill() as _}
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-300 dark:text-gray-500"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 22 20"
|
||||
>
|
||||
<path
|
||||
d="M20.924 7.625a1.523 1.523 0 0 0-1.238-1.044l-5.051-.734-2.259-4.577a1.534 1.534 0 0 0-2.752 0L7.365 5.847l-5.051.734A1.535 1.535 0 0 0 1.463 9.2l3.656 3.563-.863 5.031a1.532 1.532 0 0 0 2.226 1.616L11 17.033l4.518 2.375a1.534 1.534 0 0 0 2.226-1.617l-.863-5.03L20.537 9.2a1.523 1.523 0 0 0 .387-1.575Z"
|
||||
/>
|
||||
</svg>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</label>
|
||||
@ -589,34 +559,35 @@
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
|
||||
{#if filterList?.length !== 0}
|
||||
{#if filterList.length !== 0}
|
||||
<Button
|
||||
on:click={() => handleReset()}
|
||||
class="w-fit border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-start items-center px-3 py-2 text-white rounded-md truncate"
|
||||
on:click={handleReset}
|
||||
class="w-fit border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-start items-center px-3 py-2 text-white rounded-md truncate"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="inline-block w-4 h-4 mr-2"
|
||||
viewBox="0 0 21 21"
|
||||
><g
|
||||
>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><path d="M3.578 6.487A8 8 0 1 1 2.5 10.5" /><path
|
||||
d="M7.5 6.5h-4v-4"
|
||||
/></g
|
||||
></svg
|
||||
>
|
||||
>
|
||||
<path d="M3.578 6.487A8 8 0 1 1 2.5 10.5" />
|
||||
<path d="M7.5 6.5h-4v-4" />
|
||||
</g>
|
||||
</svg>
|
||||
Reset All
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Events Table -->
|
||||
<div class="z-0 mb-40">
|
||||
{#each filterList?.length === 0 ? weekday : weekdayFiltered as day, index}
|
||||
{#each displayWeekData as day, index}
|
||||
{#if index === selectedWeekday}
|
||||
{#if day?.length !== 0}
|
||||
<div class="flex flex-row items-center mt-5">
|
||||
@ -624,19 +595,15 @@
|
||||
{formattedWeekday[index]?.split(", ")[1]} · {day?.length}
|
||||
Events
|
||||
</h2>
|
||||
{#if filterList?.length !== 0}
|
||||
{#if filterList.length !== 0}
|
||||
<div
|
||||
class="ml-auto text-[1rem] sm:text-lg flex flex-row items-center relative block rounded-md px-2 py-1 focus:outline-none"
|
||||
>
|
||||
<span class="text-white">Filters</span>
|
||||
<span
|
||||
class="ml-2 rounded-full avatar w-5 h-5 text-xs font-semibold text-white text-center flex-shrink-0
|
||||
flex items-center justify-center bg-red-500 {filterList?.length !==
|
||||
0
|
||||
? 'bg-red-500'
|
||||
: 'bg-gray-600'}"
|
||||
class="ml-2 rounded-full avatar w-5 h-5 text-xs font-semibold text-white text-center flex-shrink-0 flex items-center justify-center bg-red-500"
|
||||
>
|
||||
{filterList?.length}
|
||||
{filterList.length}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
@ -651,16 +618,14 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each day as item}
|
||||
<!-- row -->
|
||||
<tr
|
||||
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border border-gray-800"
|
||||
>
|
||||
<td class="text-white text-sm sm:text-[1rem]">
|
||||
<label class="p-1.5 rounded-md">
|
||||
{item?.time}
|
||||
</label>
|
||||
<label class="p-1.5 rounded-md"
|
||||
>{item?.time}</label
|
||||
>
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="flex flex-row items-center text-sm sm:text-[1rem] whitespace-nowrap"
|
||||
>
|
||||
@ -670,93 +635,98 @@
|
||||
class="w-4 h-4 sm:w-6 sm:h-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
><mask id="circleFlagsEu0"
|
||||
><circle
|
||||
>
|
||||
<mask id="circleFlagsEu0">
|
||||
<circle
|
||||
cx="256"
|
||||
cy="256"
|
||||
r="256"
|
||||
fill="#fff"
|
||||
/></mask
|
||||
><g mask="url(#circleFlagsEu0)"
|
||||
><path
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#circleFlagsEu0)">
|
||||
<path
|
||||
fill="#0052b4"
|
||||
d="M0 0h512v512H0z"
|
||||
/><path
|
||||
/>
|
||||
<path
|
||||
fill="#ffda44"
|
||||
d="m256 100.2l8.3 25.5H291l-21.7 15.7l8.3 25.6l-21.7-15.8l-21.7 15.8l8.3-25.6l-21.7-15.7h26.8zm-110.2 45.6l24 12.2l18.9-19l-4.2 26.5l23.9 12.2l-26.5 4.2l-4.2 26.5l-12.2-24l-26.5 4.3l19-19zM100.2 256l25.5-8.3V221l15.7 21.7l25.6-8.3l-15.8 21.7l15.8 21.7l-25.6-8.3l-15.7 21.7v-26.8zm45.6 110.2l12.2-24l-19-18.9l26.5 4.2l12.2-23.9l4.2 26.5l26.5 4.2l-24 12.2l4.3 26.5l-19-19zM256 411.8l-8.3-25.5H221l21.7-15.7l-8.3-25.6l21.7 15.8l21.7-15.8l-8.3 25.6l21.7 15.7h-26.8zm110.2-45.6l-24-12.2l-18.9 19l4.2-26.5l-23.9-12.2l26.5-4.2l4.2-26.5l12.2 24l26.5-4.3l-19 19zM411.8 256l-25.5 8.3V291l-15.7-21.7l-25.6 8.3l15.8-21.7l-15.8-21.7l25.6 8.3l15.7-21.7v26.8zm-45.6-110.2l-12.2 24l19 18.9l-26.5-4.2l-12.2 23.9l-4.2-26.5l-26.5-4.2l24-12.2l-4.3-26.5l19 19z"
|
||||
/></g
|
||||
></svg
|
||||
>
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
{:else if item?.country === "UK"}
|
||||
<svg
|
||||
style="clip-path: circle(50%);"
|
||||
class="w-4 h-4 sm:w-6 sm:h-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
><mask id="circleFlagsUk0"
|
||||
><circle
|
||||
>
|
||||
<mask id="circleFlagsUk0">
|
||||
<circle
|
||||
cx="256"
|
||||
cy="256"
|
||||
r="256"
|
||||
fill="#fff"
|
||||
/></mask
|
||||
><g mask="url(#circleFlagsUk0)"
|
||||
><path
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#circleFlagsUk0)">
|
||||
<path
|
||||
fill="#eee"
|
||||
d="m0 0l8 22l-8 23v23l32 54l-32 54v32l32 48l-32 48v32l32 54l-32 54v68l22-8l23 8h23l54-32l54 32h32l48-32l48 32h32l54-32l54 32h68l-8-22l8-23v-23l-32-54l32-54v-32l-32-48l32-48v-32l-32-54l32-54V0l-22 8l-23-8h-23l-54 32l-54-32h-32l-48 32l-48-32h-32l-54 32L68 0z"
|
||||
/><path
|
||||
/>
|
||||
<path
|
||||
fill="#0052b4"
|
||||
d="M336 0v108L444 0Zm176 68L404 176h108zM0 176h108L0 68ZM68 0l108 108V0Zm108 512V404L68 512ZM0 444l108-108H0Zm512-108H404l108 108Zm-68 176L336 404v108z"
|
||||
/><path
|
||||
/>
|
||||
<path
|
||||
fill="#d80027"
|
||||
d="M0 0v45l131 131h45zm208 0v208H0v96h208v208h96V304h208v-96H304V0zm259 0L336 131v45L512 0zM176 336L0 512h45l131-131zm160 0l176 176v-45L381 336z"
|
||||
/></g
|
||||
></svg
|
||||
>
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
{:else}
|
||||
<img
|
||||
style="clip-path: circle(50%);"
|
||||
class="w-4 h-4 sm:w-6 sm:h-6"
|
||||
src={`https://hatscripts.github.io/circle-flags/flags/${item?.countryCode}.svg`}
|
||||
loading="lazy"
|
||||
alt="{item?.country} flag"
|
||||
/>
|
||||
{/if}
|
||||
<span class="text-white ml-2">
|
||||
{item?.country}
|
||||
</span>
|
||||
<span class="text-white ml-2"
|
||||
>{item?.country}</span
|
||||
>
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-start text-white text-sm sm:text-[1rem] whitespace-nowrap"
|
||||
>
|
||||
{item?.event?.length > 40
|
||||
? item?.event?.slice(0, 40) + "..."
|
||||
? item?.event.slice(0, 40) + "..."
|
||||
: item?.event}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap"
|
||||
>
|
||||
{item?.actual !== (null || "")
|
||||
{item?.actual !== null && item?.actual !== ""
|
||||
? abbreviateNumber(item?.actual)
|
||||
: "-"}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap"
|
||||
>
|
||||
{item?.consensus !== (null || "")
|
||||
{item?.consensus !== null &&
|
||||
item?.consensus !== ""
|
||||
? abbreviateNumber(item?.consensus)
|
||||
: "-"}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap"
|
||||
>
|
||||
{item?.prior !== (null || "")
|
||||
{item?.prior !== null && item?.prior !== ""
|
||||
? abbreviateNumber(item?.prior)
|
||||
: "-"}
|
||||
</td>
|
||||
|
||||
<td
|
||||
class="text-white text-start text-sm sm:text-[1rem] whitespace-nowrap"
|
||||
>
|
||||
@ -818,7 +788,7 @@
|
||||
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit sm:hover:bg-secondary transition ease-out duration-100"
|
||||
>
|
||||
<a
|
||||
href={"/earnings-calendar"}
|
||||
href="/earnings-calendar"
|
||||
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
|
||||
>
|
||||
<div class="w-full flex justify-between items-center p-3 mt-3">
|
||||
@ -827,9 +797,9 @@
|
||||
</h2>
|
||||
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
|
||||
</div>
|
||||
<span class="text-white p-3 ml-3 mr-3">
|
||||
Get the latest Earnings of companies
|
||||
</span>
|
||||
<span class="text-white p-3 ml-3 mr-3"
|
||||
>Get the latest Earnings of companies</span
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -837,7 +807,7 @@
|
||||
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit sm:hover:bg-secondary transition ease-out duration-100"
|
||||
>
|
||||
<a
|
||||
href={"/dividends-calendar"}
|
||||
href="/dividends-calendar"
|
||||
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
|
||||
>
|
||||
<div class="w-full flex justify-between items-center p-3 mt-3">
|
||||
@ -846,9 +816,9 @@
|
||||
</h2>
|
||||
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
|
||||
</div>
|
||||
<span class="text-white p-3 ml-3 mr-3">
|
||||
Get the latest dividend announcement
|
||||
</span>
|
||||
<span class="text-white p-3 ml-3 mr-3"
|
||||
>Get the latest dividend announcement</span
|
||||
>
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@ -3,7 +3,7 @@ import { convertToSlug } from "$lib/utils";
|
||||
const pages = [
|
||||
{ title: "/" },
|
||||
{ title: "/reddit-tracker" },
|
||||
{ title: "/list/most-shorted-stocks"},
|
||||
{ title: "/list/most-shorted-stocks" },
|
||||
{ title: "/stocks" },
|
||||
{ title: "/etf" },
|
||||
{ title: "/etf/etf-providers" },
|
||||
@ -29,10 +29,10 @@ const pages = [
|
||||
{ title: "/list/market-cap/small-cap-stocks" },
|
||||
{ title: "/list/market-cap/micro-cap-stocks" },
|
||||
{ title: "/list/market-cap/nano-cap-stocks" },
|
||||
{ title: "/list/highest-open-interest" },
|
||||
{ title: "/list/highest-open-interest-change" },
|
||||
{ title: "/list/highest-option-iv-rank" },
|
||||
{ title: "/list/highest-option-premium" },
|
||||
{ title: "/list/highest-open-interest" },
|
||||
{ title: "/list/highest-open-interest-change" },
|
||||
{ title: "/list/highest-option-iv-rank" },
|
||||
{ title: "/list/highest-option-premium" },
|
||||
{ title: "/list/bitcoin-etfs" },
|
||||
{ title: "/stock-screener" },
|
||||
{ title: "/market-news" },
|
||||
@ -66,7 +66,7 @@ const pages = [
|
||||
{ title: "/analysts" },
|
||||
{ title: "/analysts/top-stocks" },
|
||||
{ title: "/heatmap" },
|
||||
{ title: "/market-flow" },
|
||||
{ title: "/market-flow" },
|
||||
];
|
||||
|
||||
const website = "https://stocknear.com";
|
||||
@ -76,8 +76,6 @@ export async function GET({ locals }) {
|
||||
//get all posts;
|
||||
const { apiKey, apiURL, pb } = locals;
|
||||
|
||||
|
||||
|
||||
const rawData = await fetch(apiURL + "/full-searchbar", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
@ -92,16 +90,15 @@ export async function GET({ locals }) {
|
||||
type: item?.type,
|
||||
}));
|
||||
|
||||
const articles = await pb.collection("articles").getFullList({
|
||||
sort: "-created",
|
||||
});
|
||||
|
||||
const articles = await pb.collection("articles").getFullList({
|
||||
sort: "-created",
|
||||
});
|
||||
|
||||
const tutorials = await pb.collection("tutorials").getFullList({
|
||||
sort: "-created",
|
||||
});
|
||||
const tutorials = await pb.collection("tutorials").getFullList({
|
||||
sort: "-created",
|
||||
});
|
||||
|
||||
const body = sitemap(stocks, articles, pages, tutorials);
|
||||
const body = sitemap(stocks, articles, pages);
|
||||
const response = new Response(body);
|
||||
response.headers.set("Cache-Control", "max-age=0, s-maxage=3600");
|
||||
response.headers.set("Content-Type", "application/xml");
|
||||
@ -113,7 +110,6 @@ const sitemap = (
|
||||
stocks,
|
||||
articles,
|
||||
pages,
|
||||
tutorials,
|
||||
) => `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<urlset
|
||||
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
@ -133,23 +129,23 @@ const sitemap = (
|
||||
)
|
||||
.join("")}
|
||||
${articles
|
||||
.map(
|
||||
(item) => `
|
||||
.map(
|
||||
(item) => `
|
||||
<url>
|
||||
<loc>${website}/blog/article/${convertToSlug(item?.title)}</loc>
|
||||
</url>
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
)
|
||||
.join("")}
|
||||
${tutorials
|
||||
.map(
|
||||
(item) => `
|
||||
.map(
|
||||
(item) => `
|
||||
<url>
|
||||
<loc>${website}/learning-center/article/${convertToSlug(item?.title)}</loc>
|
||||
</url>
|
||||
`,
|
||||
)
|
||||
.join("")}
|
||||
)
|
||||
.join("")}
|
||||
${stocks
|
||||
.map((ticker) => {
|
||||
// Determine the path based on the type of the ticker
|
||||
@ -158,7 +154,7 @@ const sitemap = (
|
||||
? "/stocks/"
|
||||
: ticker.type === "ETF"
|
||||
? "/etf/"
|
||||
: "/index/";
|
||||
: "/crypto/";
|
||||
return `
|
||||
<url>
|
||||
<loc>${website}${path}${ticker.id}</loc>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user