add info text to screener

This commit is contained in:
MuslemRahimi 2024-09-15 16:58:57 +02:00
parent a4c23e1381
commit 6f91c112dc
3 changed files with 100 additions and 9 deletions

View File

@ -0,0 +1,27 @@
<script lang="ts">
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
import { cn, flyAndScale } from "$lib/utils.js";
type $$Props = HoverCardPrimitive.ContentProps;
let className: $$Props["class"] = undefined;
export let align: $$Props["align"] = "center";
export let sideOffset: $$Props["sideOffset"] = 4;
export let transition: $$Props["transition"] = flyAndScale;
export let transitionConfig: $$Props["transitionConfig"] = undefined;
export { className as class };
</script>
<HoverCardPrimitive.Content
{transition}
{transitionConfig}
{sideOffset}
{align}
class={cn(
"bg-popover text-popover-foreground z-50 w-64 rounded-md border p-4 shadow-md outline-none",
className
)}
{...$$restProps}
>
<slot />
</HoverCardPrimitive.Content>

View File

@ -0,0 +1,14 @@
import { LinkPreview as HoverCardPrimitive } from "bits-ui";
import Content from "./hover-card-content.svelte";
const Root = HoverCardPrimitive.Root;
const Trigger = HoverCardPrimitive.Trigger;
export {
Root,
Content,
Trigger,
Root as HoverCard,
Content as HoverCardContent,
Trigger as HoverCardTrigger,
};

View File

@ -1,11 +1,12 @@
<script lang='ts'> <script lang='ts'>
import { onMount, onDestroy } from 'svelte'; import { onMount, onDestroy } from 'svelte';
import { goto} from '$app/navigation'; import { goto} from '$app/navigation';
import { screenWidth, numberOfUnreadNotification, strategyId} from '$lib/store'; import { clearCache, screenWidth, numberOfUnreadNotification, getCache, setCache} from '$lib/store';
import toast from 'svelte-french-toast'; import toast from 'svelte-french-toast';
import { abbreviateNumber, sectorList, industryList, listOfRelevantCountries } from '$lib/utils'; import { abbreviateNumber, sectorList, industryList, listOfRelevantCountries } from '$lib/utils';
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js"; import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
import { Button } from "$lib/components/shadcn/button/index.js"; import { Button } from "$lib/components/shadcn/button/index.js";
import * as HoverCard from "$lib/components/shadcn/hover-card/index.js";
import Input from '$lib/components/Input.svelte'; import Input from '$lib/components/Input.svelte';
//const userConfirmation = confirm('Unsaved changes detected. Leaving now will discard your strategy. Continue?'); //const userConfirmation = confirm('Unsaved changes detected. Leaving now will discard your strategy. Continue?');
@ -20,6 +21,7 @@
let syncWorker: Worker | undefined; let syncWorker: Worker | undefined;
let downloadWorker: Worker | undefined; let downloadWorker: Worker | undefined;
let searchQuery = ''; let searchQuery = '';
let infoText = {};
$: testList = []; $: testList = [];
@ -43,11 +45,11 @@
const allRules = { const allRules = {
avgVolume: { label: 'Average Volume', step: ['100M','10M','1M','100K','10K','1K','0'], category: 'fund', defaultCondition: 'over', defaultValue: 0 }, avgVolume: { label: 'Average Volume', step: ['100M','10M','1M','100K','10K','1K','0'], category: 'fund', defaultCondition: 'over', defaultValue: 0 },
volume: { label: 'Volume', step: ['100M','10M','1M','100K','10K','1K','0'], category: 'fund', defaultCondition: 'over', defaultValue: 0 }, volume: { label: 'Volume', step: ['100M','10M','1M','100K','10K','1K','0'], category: 'fund', defaultCondition: 'over', defaultValue: 0 },
rsi: { label: 'RSI', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 }, rsi: { label: 'Relative Strength Index', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 },
stochRSI: { label: 'Stoch RSI Fast', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 }, stochRSI: { label: 'Stochastic RSI Fast', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 },
mfi: { label: 'MFI', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 }, mfi: { label: 'Money Flow Index', step: [90,80,70,60,50,40,30,20], category: 'ta', defaultCondition: 'over', defaultValue: 40 },
cci: { label: 'CCI', step: [250,200,100,50,20,0,-20,-50,-100,-200,-250], category: 'ta', defaultCondition: 'over', defaultValue: 0 }, cci: { label: 'Commodity Channel Index', step: [250,200,100,50,20,0,-20,-50,-100,-200,-250], category: 'ta', defaultCondition: 'over', defaultValue: 0 },
atr: { label: 'ATR', step: [20,15,10,5,3,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 }, atr: { label: 'Average True Range', step: [20,15,10,5,3,1], category: 'ta', defaultCondition: 'over', defaultValue: 10 },
sma20: { label: 'SMA20', step: ['Stock Price > SMA20', 'SMA20 > SMA50', 'SMA20 > SMA100', 'SMA20 > SMA200'], category: 'ta', defaultValue: 'any' }, sma20: { label: 'SMA20', step: ['Stock Price > SMA20', 'SMA20 > SMA50', 'SMA20 > SMA100', 'SMA20 > SMA200'], category: 'ta', defaultValue: 'any' },
sma50: { label: 'SMA50', step: ['Stock Price > SMA50', 'SMA50 > SMA20', 'SMA50 > SMA100', 'SMA50 > SMA200'], category: 'ta', defaultValue: 'any' }, sma50: { label: 'SMA50', step: ['Stock Price > SMA50', 'SMA50 > SMA20', 'SMA50 > SMA100', 'SMA50 > SMA200'], category: 'ta', defaultValue: 'any' },
sma100: { label: 'SMA100', step: ['Stock Price > SMA100', 'SMA100 > SMA20', 'SMA100 > SMA50', 'SMA100 > SMA200'], category: 'ta', defaultValue: 'any' }, sma100: { label: 'SMA100', step: ['Stock Price > SMA100', 'SMA100 > SMA20', 'SMA100 > SMA50', 'SMA100 > SMA200'], category: 'ta', defaultValue: 'any' },
@ -210,6 +212,27 @@ $: allRows = Object?.entries(allRules)
}); });
async function getInfoText(parameter) {
const cachedData = getCache(parameter, "getInfoText");
if (cachedData) {
infoText = cachedData;
} else {
const postData = { parameter };
const response = await fetch(data?.apiURL + "/info-text", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
infoText = await response.json();
setCache(parameter, infoText, "getInfoText");
}
};
async function handleCreateStrategy() { async function handleCreateStrategy() {
if(data?.user?.tier === 'Pro' && !data?.user?.freeTrial) { if(data?.user?.tier === 'Pro' && !data?.user?.freeTrial) {
const closePopup = document.getElementById("addStrategy"); const closePopup = document.getElementById("addStrategy");
@ -532,6 +555,7 @@ onMount(async () => {
onDestroy(() => { onDestroy(() => {
syncWorker?.terminate(); syncWorker?.terminate();
syncWorker = undefined; syncWorker = undefined;
clearCache();
}); });
@ -1011,9 +1035,35 @@ function handleInput(event) {
{#each displayRules as row (row?.rule)} {#each displayRules as row (row?.rule)}
<!--Start Added Rules--> <!--Start Added Rules-->
<div class="flex items-center justify-between space-x-2 px-1 py-1.5 text-smaller leading-tight text-default"> <div class="flex items-center justify-between space-x-2 px-1 py-1.5 text-smaller leading-tight text-default">
<div class="text-white text-[1rem]"> <div class="text-white text-[1rem] relative">
{row?.label?.replace('[%]','')} {row?.label?.replace('[%]', '')}
</div> <HoverCard.Root>
<HoverCard.Trigger
class="rounded-sm underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-8 focus-visible:outline-black">
<label on:mouseover={() => getInfoText(row?.rule)} class="relative" role="tooltip">
<span class="absolute -right-[15px] -top-[3px] cursor-pointer p-1 text-gray-300 sm:hover:text-white">
<svg class="h-[10.5px] w-[10.5px]" viewBox="0 0 4 16" fill="currentColor" style="max-width:20px"><path d="M0 6h4v10h-4v-10zm2-6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"></path></svg>
</span>
</label>
</HoverCard.Trigger>
<HoverCard.Content class="w-96">
<div class="flex justify-between space-x-4 w-full">
<div class="space-y-1 w-full">
<h4 class="text-lg font-semibold pb-1 border-b border-gray-400 ">{row?.label?.replace('[%]', '')}</h4>
<div class="text-sm w-full pt-2 text-black">
{infoText?.text ?? 'n/a'}
</div>
{#if infoText?.equation !== undefined}
<div class="text-sm w-full pt-2 text-black border-t border-gray-400">
{infoText?.equation}
</div>
{/if}
</div>
</div>
</HoverCard.Content>
</HoverCard.Root>
</div>
<div class="flex items-center"> <div class="flex items-center">
<button on:click={() => handleDeleteRule(row?.rule)} class="mr-1.5 cursor-pointer text-gray-300 sm:hover:text-red-500 focus:outline-none" title="Remove filter"> <button on:click={() => handleDeleteRule(row?.rule)} class="mr-1.5 cursor-pointer text-gray-300 sm:hover:text-red-500 focus:outline-none" title="Remove filter">
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="CurrentColor" style="max-width:40px"> <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="CurrentColor" style="max-width:40px">