From 236cd95871366b29ace099d2db23c3d16dc9630b Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Thu, 12 Dec 2024 14:54:00 +0100 Subject: [PATCH] sorting watchlist --- src/routes/watchlist/stocks/+page.svelte | 187 ++++++++++++++++++++--- 1 file changed, 166 insertions(+), 21 deletions(-) diff --git a/src/routes/watchlist/stocks/+page.svelte b/src/routes/watchlist/stocks/+page.svelte index 8d15514d..d886ad99 100644 --- a/src/routes/watchlist/stocks/+page.svelte +++ b/src/routes/watchlist/stocks/+page.svelte @@ -16,6 +16,7 @@ import { Combobox } from "bits-ui"; import HoverStockChart from "$lib/components/HoverStockChart.svelte"; import { goto } from "$app/navigation"; + import TableHeader from "$lib/components/Table/TableHeader.svelte"; export let data; let timeoutId; @@ -30,6 +31,7 @@ let deleteTickerList = []; let watchList: any[] = []; + let originalData = []; let news = []; let earnings = []; @@ -163,6 +165,11 @@ const handleDownloadMessage = (event) => { isLoaded = false; watchList = event?.data?.watchlistData ?? []; + originalData = event?.data?.watchlistData ?? []; + if (watchList?.length > 0) { + columns = generateColumns(watchList); + sortOrders = generateSortOrders(watchList); + } isLoaded = true; }; @@ -236,6 +243,8 @@ const output = await response?.json(); watchList = output?.data; + originalData = output?.data; + news = output?.news; earnings = output?.earnings; @@ -249,6 +258,9 @@ return match ? { ...item, name: match?.name } : { ...item }; }); if (watchList?.length > 0) { + columns = generateColumns(watchList); + sortOrders = generateSortOrders(watchList); + groupedEarnings = groupEarnings(earnings); groupedNews = groupNews(news, watchList); } else { @@ -412,6 +424,8 @@ (item) => !deleteTickerList?.includes(item?.symbol), ); + originalData = watchList; + news = news?.filter((item) => !deleteTickerList?.includes(item?.symbol)); earnings = earnings?.filter( (item) => !deleteTickerList?.includes(item?.symbol), @@ -443,6 +457,8 @@ allList = [...allList]; if (watchList?.length > 0) { + columns = generateColumns(watchList); + sortOrders = generateSortOrders(watchList); groupedNews = groupNews(news, watchList); groupedEarnings = groupEarnings(earnings); } else { @@ -470,6 +486,9 @@ style: "border-radius: 10px; background: #333; color: #fff; padding: 12px; margin-top: 10px; box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);", }); + + inputValue = ""; + event.preventDefault(); return; } @@ -820,6 +839,150 @@ searchBarData = await response?.json(); }, 50); // delay } + + // Function to generate columns based on keys in rawData + function generateColumns(data) { + const leftAlignKeys = new Set(["rank", "symbol", "name"]); + + // Custom labels for specific keys + const customLabels = { + changesPercentage: "% Change", + score: "AI Score", + researchAndDevelopmentExpenses: "R&D", + counter: "Ratings Count", + // Add more key-label mappings here as needed + }; + + // Define preferred order for columns + const preferredOrder = ["rank", "symbol", "name"]; + + // Create a mapping of rule to name and type from allRows + const ruleToMetadataMap = Object.fromEntries( + allRows.map((row) => [row.rule, { name: row.name, type: row.type }]), + ); + + // Separate preferred keys and other keys, excluding "type" + const keys = Object?.keys(data?.at(0))?.filter((key) => key !== "type"); + + // Merge the preferred order with the default list order + const orderedKeys = [ + ...preferredOrder?.filter((key) => keys?.includes(key)), + ...ruleOfList + ?.map((item) => item?.rule) + ?.filter((key) => keys?.includes(key)), + ...keys?.filter( + (key) => + !preferredOrder?.includes(key) && + !ruleOfList?.some((item) => item?.rule === key), + ), + ]; + + return orderedKeys?.map((key) => ({ + key, + label: + customLabels[key] || + ruleToMetadataMap[key]?.name || // Check allRows mapping first + key?.charAt(0)?.toUpperCase() + + key?.slice(1)?.replace(/([A-Z])/g, " $1"), + type: ruleToMetadataMap[key]?.type || "string", // Add type from allRows or default to 'string' + align: leftAlignKeys?.has(key) ? "left" : "right", + })); + } + + // Function to generate sortOrders based on keys in rawData + function generateSortOrders(data) { + const stringKeys = new Set([ + "symbol", + "name", + "industry", + "score", + "sector", + "analystRating", + ]); + + return Object.keys(data[0])?.reduce((orders, key) => { + orders[key] = { + order: "none", + type: stringKeys.has(key) ? "string" : "number", + }; + return orders; + }, {}); + } + + // Generate columns and sortOrders + let columns; + let sortOrders; + + const sortData = (key, input = false) => { + // Reset all other keys to 'none' except the current key + for (const k in sortOrders) { + if (k !== key) { + sortOrders[k].order = "none"; + } + } + + // If input is false, cycle through 'none', 'asc', 'desc' for the clicked key + const orderCycle = ["none", "asc", "desc"]; + + const currentOrderIndex = orderCycle.indexOf( + sortOrders[key]?.order || "none", + ); + 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 + if (sortOrder === "none") { + watchList = [...originalData]; // Reset originalData to rawData + 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]); + break; + case "rating": + case "string": + valueA = a[key]; + valueB = b[key]; + if (valueA == null && valueB == null) return 0; + if (valueA == null) return 1; + if (valueB == null) return -1; + valueA = valueA?.toUpperCase(); + valueB = valueB?.toUpperCase(); + return sortOrder === "asc" + ? valueA?.localeCompare(valueB) + : valueB?.localeCompare(valueA); + case "number": + default: + valueA = parseFloat(a[key]); + valueB = parseFloat(b[key]); + break; + } + return sortOrder === "asc" + ? valueA < valueB + ? -1 + : valueA > valueB + ? 1 + : 0 + : valueA > valueB + ? -1 + : valueA < valueB + ? 1 + : 0; + }; + + // Sort and update the originalData and stockList + watchList = [...originalData].sort(compareValues)?.slice(0, 50); + }; @@ -1069,8 +1232,8 @@ {#each searchBarData as item} handleAddTicker(e, item?.symbol)} >
@@ -1293,25 +1456,7 @@ > - - Symbol - Company - - {#each ruleOfList as item} - {#if isChecked(item?.name)} - {item?.name} - {/if} - {/each} - + {#each watchList as item}