improve economic calendar

This commit is contained in:
MuslemRahimi 2024-09-11 00:51:48 +02:00
parent f9b74774b1
commit 61713839ee
4 changed files with 178 additions and 90 deletions

View File

@ -2,9 +2,11 @@
import { format, startOfWeek, addDays, addWeeks, subWeeks, differenceInWeeks } from 'date-fns'; import { format, startOfWeek, addDays, addWeeks, subWeeks, differenceInWeeks } from 'date-fns';
import { screenWidth, numberOfUnreadNotification } from '$lib/store'; import { screenWidth, numberOfUnreadNotification } from '$lib/store';
import logo from '$lib/images/transcripts_logo.png'; import logo from '$lib/images/transcripts_logo.png';
import { listOfCountries } from '$lib/utils'; import { abbreviateNumber, listOfRelevantCountries } from '$lib/utils';
import ArrowLogo from "lucide-svelte/icons/move-up-right"; import ArrowLogo from "lucide-svelte/icons/move-up-right";
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
import { Button } from "$lib/components/shadcn/button/index.js";
export let data; export let data;
let rawData; let rawData;
@ -17,6 +19,8 @@
let currentWeek = startOfWeek(today, { weekStartsOn: 1 }); let currentWeek = startOfWeek(today, { weekStartsOn: 1 });
let previousMax = false; let previousMax = false;
let nextMax = false; let nextMax = false;
let searchQuery = '';
$: testList = [];
$: economicCalendar = data?.getEconomicCalendar; $: economicCalendar = data?.getEconomicCalendar;
$: daysOfWeek = getDaysOfWeek(currentWeek); $: daysOfWeek = getDaysOfWeek(currentWeek);
@ -45,17 +49,6 @@
}); });
} }
async function handleFilter(e, newFilter) {
const filterSet = new Set(filterList);
filterSet.has(newFilter) ? filterSet.delete(newFilter) : filterSet.add(newFilter);
filterList = Array.from(filterSet);
if (filterList.length !== 0) {
await loadWorker();
} else {
weekday = rawData;
}
}
const handleMessage = (event) => { const handleMessage = (event) => {
weekdayFiltered = event.data?.finalData?.output ?? []; weekdayFiltered = event.data?.finalData?.output ?? [];
@ -93,6 +86,50 @@
function changeWeek(state) { function changeWeek(state) {
currentWeek = state === 'previous' ? subWeeks(currentWeek, 1) : addWeeks(currentWeek, 1); currentWeek = state === 'previous' ? subWeeks(currentWeek, 1) : addWeeks(currentWeek, 1);
} }
function handleInput(event) {
const searchQuery = event.target.value?.toLowerCase() || '';
setTimeout(() => {
testList = [];
if (searchQuery.length > 0) {
const rawList = listOfRelevantCountries;
testList = rawList?.filter(item => {
const index = item?.toLowerCase();
// Check if country starts with searchQuery
return index?.startsWith(searchQuery);
}) || [];
}
}, 50);
}
$: checkedItems = new Set();
async function handleChangeValue(value) {
if (checkedItems.has(value)) {
checkedItems.delete(value);
} else {
checkedItems.add(value);
}
const filterSet = new Set(filterList);
filterSet.has(value) ? filterSet.delete(value) : filterSet.add(value);
filterList = Array.from(filterSet);
if (filterList.length !== 0) {
await loadWorker();
} else {
weekday = rawData;
}
}
</script> </script>
<svelte:head> <svelte:head>
@ -226,60 +263,48 @@
<div class="flex flex-row items-center w-fit ml-auto mt-6 mb-2 mr-3 sm:mr-0"> <div class="flex flex-row items-center w-fit ml-auto mt-6 mb-2 mr-3 sm:mr-0">
{#if filterList?.length !== 0} {#if filterList?.length !== 0}
<label on:click={() => filterList = [] } class="mr-3 text-sm cursor-pointer bg-[#27272A] sm:hover:bg-[#27272A] text-white duration-100 transition ease-in-out px-4 py-2 rounded-lg shadow-lg ml-auto"> <label on:click={() => {filterList = []; checkedItems = new Set();} } class="mr-3 text-sm cursor-pointer bg-[#27272A] sm:hover:bg-[#27272A] text-white duration-100 transition ease-in-out px-4 py-2 rounded-lg shadow-lg ml-auto">
<svg xmlns="http://www.w3.org/2000/svg" class="inline-block w-4 h-4" viewBox="0 0 21 21"><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> <svg xmlns="http://www.w3.org/2000/svg" class="inline-block w-4 h-4" viewBox="0 0 21 21"><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>
Reset All Reset All
</label> </label>
{/if} {/if}
<div class="dropdown dropdown-end z-30"> <DropdownMenu.Root>
<button tabindex="0" role="button" class="text-sm cursor-pointer text-white bg-[#27272A] sm:hover:bg-[#27272A] duration-100 transition ease-in-out px-4 py-2 rounded-lg shadow-lg ml-auto"> <DropdownMenu.Trigger asChild let:builder>
Filter <Button builders={[builder]} class="border-gray-600 border bg-[#09090B] sm:hover:bg-[#27272A] ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-lg truncate">
<svg class="inline-block w-2.5 h-2.5 ml-1" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6"> <span class="truncate text-white">Filter</span>
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 4 4 4-4"/> <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> </svg>
</button> </Button>
</DropdownMenu.Trigger>
<!-- Dropdown menu --> <DropdownMenu.Content class="w-56 h-fit max-h-72 overflow-y-auto scroller">
<div class="relative sticky z-40 focus:outline-none -top-1"
<ul tabindex="0" class="z-30 dropdown-content p-2 shadow bg-[#1D232A] rounded w-60 h-72 oveflow-hidden overflow-y-scroll"> tabindex="0" role="menu" style="">
<input bind:value={searchQuery}
<div class="mb-3 mt-1 ml-1"> on:input={handleInput}
<span class="text-white text-sm mb-2"> autocomplete="off"
Popular class=" absolute fixed sticky w-full border-0 bg-[#09090B] border-b border-gray-200
</span> focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300"
<hr class="mt-2 mb-2 border-gray-500"/> type="search"
{#each ['United States','Russia','China','UK','EU'] as item} placeholder="Search...">
<li> </div>
<label on:click|stopPropagation={(event) => handleFilter(event, item)} class="flex items-center ps-2 rounded"> <DropdownMenu.Group>
<input checked={filterList?.includes(item)} type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2"> {#each (testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : listOfRelevantCountries ) as item}
<label class="w-full py-2 ms-2 text-sm font-medium text-white rounded cursor-pointer"> <DropdownMenu.Item class="sm:hover:bg-[#27272A]">
{item} <div class="flex items-center" >
<label on:click={() => {handleChangeValue(item)}} class="cursor-pointer text-white" for={item}>
<input type="checkbox" checked={checkedItems?.has(item)}>
<span class="ml-2">{item}</span>
</label> </label>
</label> </div>
</li> </DropdownMenu.Item>
{/each} {/each}
</div> </DropdownMenu.Group>
<div> </DropdownMenu.Content>
<span class="text-white text-sm mb-2 ml-1"> </DropdownMenu.Root>
All Countries
</span>
<hr class="mt-2 mb-2 border-gray-500"/>
</div>
{#each listOfCountries as item}
<li>
<div on:click|stopPropagation={(event) => handleFilter(event, item)} class="flex items-center ps-2 rounded">
<input type="checkbox" checked={filterList?.includes(item)} class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-2">
<label class="w-full py-2 ms-2 text-sm font-medium text-white rounded cursor-pointer">
{item}
</label>
</div>
</li>
{/each}
</ul>
</div>
</div> </div>
@ -294,12 +319,12 @@
<tr class="whitespace-nowrap"> <tr class="whitespace-nowrap">
<th class="text-start text-white font-semibold text-sm"></th> <th class="text-start text-white font-semibold text-sm"></th>
<th class="text-start text-white font-semibold text-sm"></th> <th class="text-start text-white font-semibold text-sm sm:text-[1rem]"></th>
<th class="text-start text-white font-semibold text-sm">Event</th> <th class="text-start text-white font-semibold text-sm sm:text-[1rem]">Event</th>
<th class="text-end text-white font-semibold text-sm">Previous</th> <th class="text-end text-white font-semibold text-sm sm:text-[1rem]">Previous</th>
<th class="text-end text-white font-semibold text-sm">Estimated</th> <th class="text-end text-white font-semibold text-sm sm:text-[1rem]">Forecast</th>
<th class="text-end text-white font-semibold text-sm">Actual</th> <th class="text-end text-white font-semibold text-sm sm:text-[1rem]">Actual</th>
<th class="text-white font-semibold text-sm text-end">Impact</th> <th class="text-white font-semibold text-sm sm:text-[1rem] text-end">Importance</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -326,26 +351,42 @@
</span> </span>
</td> </td>
<td class="text-start text-white border-b-[#09090B] text-sm sm:text-[1rem] whitespace-nowrap"> <td class="text-start text-white border-b-[#09090B] text-sm sm:text-[1rem] whitespace-nowrap">
{item?.event?.length > 15 ? item?.event?.slice(0,15) + '...' : item?.event} {item?.event?.length > 30 ? item?.event?.slice(0,30) + '...' : item?.event}
</td> </td>
<td class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap"> <td class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap">
{item?.previous !== null ? item?.previous : '-'} {item?.prior !== (null || '') ? abbreviateNumber(item?.prior) : '-'}
</td> </td>
<td class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap"> <td class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap">
{item?.estimate !== null ? item?.estimate : '-'} {item?.consensus !== (null || '') ? abbreviateNumber(item?.consensus) : '-'}
</td> </td>
<td class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap"> <td class="text-white border-b-[#09090B] text-end text-sm sm:text-[1rem] whitespace-nowrap">
{item?.actual !== null ? item?.actual : '-'} {item?.actual !== null ? abbreviateNumber(item?.actual) : '-'}
</td> </td>
<td class="{item?.impact === 'Low' ? 'text-[#00FC50]' : item?.impact === 'Medium' ? 'text-[#3DDBFE]' : item?.impact === 'High' ? 'text-[#FC2120]' : 'text-white'} text-end text-sm sm:text-[1rem] whitespace-nowrap border-b-[#09090B]"> <td class="text-white text-start text-sm sm:text-[1rem] whitespace-nowrap border-b-[#09090B]">
{item?.impact} <div class="flex flex-row items-center justify-end">
{#each Array.from({ length: 3 }) as _, i}
{#if i < Math.floor(item?.importance)}
<svg class="w-4 h-4 text-[#FFA500]" 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>
{:else}
<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>
{/if}
{/each}
</div>
</td> </td>
</tr> </tr>

View File

@ -1,29 +1,28 @@
import { getCache, setCache } from '$lib/store'; import { getCache, setCache } from "$lib/store";
export const load = async ({ parent }) => {
export const load = async ({parent}) => {
const getEconomicCalendar = async () => { const getEconomicCalendar = async () => {
let output; let output;
// Get cached data for the specific tickerID // Get cached data for the specific tickerID
const cachedData = getCache('', 'getEconomicCalendar'); const cachedData = getCache("", "getEconomicCalendar");
if (cachedData) { if (cachedData) {
output = cachedData; output = cachedData;
} else { } else {
const { apiURL, apiKey } = await parent(); const { apiURL, apiKey } = await parent();
// make the POST request to the endpoint // make the POST request to the endpoint
const response = await fetch(apiURL + '/economic-calendar', { const response = await fetch(apiURL + "/economic-calendar", {
method: 'GET', method: "GET",
headers: { headers: {
"Content-Type": "application/json", "X-API-KEY": apiKey "Content-Type": "application/json",
"X-API-KEY": apiKey,
}, },
}); });
output = await response.json(); output = await response.json();
// Cache the data for this specific tickerID with a specific name 'getEconomicCalendar' // Cache the data for this specific tickerID with a specific name 'getEconomicCalendar'
setCache('', output, 'getEconomicCalendar'); setCache("", output, "getEconomicCalendar");
} }
return output; return output;
@ -31,6 +30,6 @@ export const load = async ({parent}) => {
// Make sure to return a promise // Make sure to return a promise
return { return {
getEconomicCalendar: await getEconomicCalendar() getEconomicCalendar: await getEconomicCalendar(),
}; };
}; };

View File

@ -1,17 +1,66 @@
export const listOfRelevantCountries = [
{ USA: "United States" },
{ CHN: "China" },
{ CAN: "Canada" },
{ GBR: "United Kingdom" },
{ JPN: "Japan" },
{ ISR: "Israel" },
{ BRA: "Brazil" },
{ FRA: "France" },
{ IRL: "Ireland" },
{ DEU: "Germany" },
{ MEX: "Mexico" },
{ IND: "India" },
{ AUS: "Australia" },
{ KOR: "South Korea" },
{ SWE: "Sweden" },
{ NLD: "Netherlands" },
{ CHE: "Switzerland" },
{ TWN: "Taiwan" },
{ ZAF: "South Africa" },
{ HKG: "Hong Kong" },
{ SGP: "Singapore" },
{ ARG: "Argentina" },
{ CHL: "Chile" },
{ PHL: "Philippines" },
{ TUR: "Turkey" },
{ ITA: "Italy" },
{ IDN: "Indonesia" },
{ MYS: "Malaysia" },
{ LUX: "Luxembourg" },
{ VNM: "Vietnam" },
{ NZL: "New Zealand" },
{ DNK: "Denmark" },
{ NOR: "Norway" },
{ FIN: "Finland" },
{ RUS: "Russia" },
{ ARE: "United Arab Emirates" },
];
const countryMap = Object.fromEntries(
listOfRelevantCountries.map((entry) => {
const [code, name] = Object.entries(entry)[0];
return [name, code];
})
);
onmessage = async (event: MessageEvent) => { onmessage = async (event: MessageEvent) => {
const rawData = event.data?.rawData; const rawData = event.data?.rawData;
const filterList = event.data?.filterList; const filterList = event.data?.filterList;
const output = rawData?.map(subArray =>
subArray?.filter(item => filterList?.includes(item?.country)) // Map filterList country names to abbreviations
const filterCodes = filterList?.map((name) => countryMap[name]) || [];
// Filter rawData based on the mapped country codes
const output = rawData?.map((subArray) =>
subArray?.filter((item) => filterCodes.includes(item?.country))
); );
let finalData = { output}; let finalData = { output };
postMessage({ message: 'success', finalData}); postMessage({ message: "success", finalData });
// Sending data back to the main thread // Sending data back to the main thread
//postMessage({ message: 'Data received in the worker', ticker, apiURL }); //postMessage({ message: 'Data received in the worker', ticker, apiURL });
}; };
export {}; export {};

View File

@ -549,7 +549,6 @@ async function handleChangeValue(value) {
console.warn(`Unhandled rule: ${ruleName}`); console.warn(`Unhandled rule: ${ruleName}`);
} }
console.log(valueMappings[ruleName])
} }