Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: search Issue, added loader & debouncing #13

Open
wants to merge 3 commits into
base: nms-helper-ui
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/components/Loader.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<svg
class="animate-spin -ml-1 mr-3 h-5 w-5 text-plblack"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
30 changes: 30 additions & 0 deletions src/components/Navbar.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<nav class="bg-gray-800">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="border-b border-gray-700">
<div class="flex h-16 items-center justify-between px-4 sm:px-0">
<div class="flex items-center">
<div class="flex-shrink-0">
<img class="h-8 w-32" src="/powerloom-logo.svg" alt="PowerLoom Logo" />
</div>
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<a
href="/"
class={'bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium'}
aria-current="page">Dashboard</a
>
<!-- <a
href="/"
class={slug == ''
? 'bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium'
: 'text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium'}
aria-current="page">Dashboard</a
> -->
</div>
</div>
</div>
</div>
</div>
</div>
</nav>
225 changes: 225 additions & 0 deletions src/components/SearchSlot.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<script lang="ts">
import axios from 'axios';
import { env } from '$env/dynamic/public';
import Loader from '../components/Loader.svelte';

export let epochIds: number[] = [];
let searchVal = '';
let isSearching = false;
let timeout: ReturnType<typeof setTimeout> | null;
let data: {
[key: number]: {
finalizedProjects: {
[key: string]: string;
};
submittedProjects: string[];
};
} = {};

export let API_PREFIX = env.PUBLIC_API_PREFIX;

async function getFinalizedProjects(epochId: number) {
let response;
try {
response = await axios.post(API_PREFIX + `/getFinalizedProjects`, { epochId: epochId });
// console.log('got Finalized Projects', response.data);
if (response.data) {
return response.data.finalized_projects;
} else {
throw new Error(JSON.stringify(response.data));
}
} catch (e) {
console.error('Unable to get finalized projects', e);
}
}

async function getSubmittedProjects(epochId: number, slotId: string) {
let response;
try {
response = await axios.post(API_PREFIX + `/getSubmittedProjects`, {
epochId: epochId,
slotId: slotId
});
// console.log('got Submitted Projects', response.data);
if (response.data) {
return response.data.submitted_projects;
} else {
throw new Error(JSON.stringify(response.data));
}
} catch (e) {
console.error('Unable to get submitted projects', e);
}
}

const loadData = async () => {
for (let i = 0; i < epochIds.length; i++) {
try {
let finalizedProjects = await getFinalizedProjects(epochIds[i]);
let submittedProjects = await getSubmittedProjects(epochIds[i], searchVal);

isSearching = false;

data[epochIds[i]] = {
finalizedProjects: finalizedProjects,
submittedProjects: submittedProjects
};
} catch (e) {
isSearching = false;
console.error('Error loading data', e);
}
}
};

const handleInput = () => {
if (searchVal.length === 0) {
data = {};
return;
}

if (epochIds.length === 0) {
return;
}

// check if searchVal is not a number
if (isNaN(Number(searchVal))) {
return;
}
/**
* Debounce the search input to avoid multiple API calls
*/
if (timeout) {
clearTimeout(timeout);
}
isSearching = true;
timeout = setTimeout(() => {
loadData();
}, 800);
};
</script>

<div class="relative mt-6 flex items-center">
<input
type="text"
name="search"
id="search"
bind:value={searchVal}
on:input={handleInput}
placeholder="Enter your slot Id"
class="block w-full rounded-md border-0 py-2 pr-14 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
<button
class="absolute right-0 flex pr-1.5"
on:click={() => {
searchVal = '';
data = {};
}}
>
<kbd
class="inline-flex items-center rounded border border-gray-200 px-1 font-sans text-xs text-gray-400"
>⌫</kbd
>
</button>
</div>

<div class="mb-4">
{#if !isSearching}
{#each Object.entries(data).sort(([keyA], [keyB]) => keyB.localeCompare(keyA)) as [key, value]}
<div class="overflow-hidden bg-white shadow sm:rounded-md mt-6">
<div class="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
<dt class="truncate text-sm font-medium text-gray-500">Epoch</dt>
<dd class="mt-1 text-3xl font-semibold tracking-tight text-gray-900">{key}</dd>
</div>
{#if value.submittedProjects.length > 0}
<ul class="divide-y divide-gray-200 mb-4">
{#each value.submittedProjects as project}
<li>
<div class="flex items-center px-4 py-4 sm:px-6">
<div class="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
<div class="truncate">
<div class="flex text-sm">
<p class="truncate font-medium text-indigo-600">
{project.split(':')[0]} for {project.split(':')[1]}
</p>
<p class="ml-1 flex-shrink-0 font-normal text-gray-500">
in {project.split(':')[2]}
</p>
</div>
</div>
<div class="mt-4 flex-shrink-0 sm:mt-0 sm:ml-5">
<div class="flex -space-x-1 overflow-hidden">
{#if value.finalizedProjects.hasOwnProperty(project)}
<span class="inline-block h-6 w-6 rounded-full bg-green-100">
<svg
class="h-6 w-6 text-green-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
</span>
{value.finalizedProjects[project]}
{/if}
{#if !value.finalizedProjects.hasOwnProperty(project)}
<span class="inline-block h-6 w-6 rounded-full bg-red-100">
<!-- question mark -->
<svg
class="h-6 w-6 text-red-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v2m0 6v2"
/>
</svg></span
>
{/if}
</div>
</div>
</div>
<div class="ml-5 flex-shrink-0">
<!-- Heroicon name: mini/chevron-right -->
<svg
class="h-5 w-5 text-gray-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z"
clip-rule="evenodd"
/>
</svg>
</div>
</div>
</li>
{/each}
</ul>
{/if}
</div>
{/each}
{:else}
<div class="flex justify-center mt-10">
<Loader />
</div>
{/if}
</div>

{#if searchVal.length === 0 && !isSearching}
<h3 class="text-lg text-center mt-6 font-medium leading-6 text-gray-900">
Please enter your Slot Id to check status
</h3>
{/if}
39 changes: 39 additions & 0 deletions src/components/TopStats.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import { env } from '$env/dynamic/public';

export let projectIdsLength;
export let currentEpochId;
export let currentEpoch;
export let activeSlots;
export let masterSnapshotterCount;

export let EXPLORER_PREFIX = env.PUBLIC_EXPLORER_PREFIX || 'https://etherscan.io';
</script>

<div>
<h3 class="text-lg font-medium leading-6 text-gray-900">Onchain consensus Stats</h3>
<dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
<div class="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
<dt class="truncate text-sm font-medium text-gray-500">Total Projects</dt>
<dd class="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
{projectIdsLength}
</dd>
</div>

<div class="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
<dt class="truncate text-sm font-medium text-gray-500">Current Epoch {currentEpochId}</dt>
<dd class="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
<a href="{EXPLORER_PREFIX}/block/{currentEpoch}" target="_blank" rel="noreferrer noopener"
>{currentEpoch}</a
>
</dd>
</div>

<div class="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
<dt class="truncate text-sm font-medium text-gray-500">Total Snapshotters</dt>
<dd class="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
{activeSlots + masterSnapshotterCount}
</dd>
</div>
</dl>
</div>
724 changes: 723 additions & 1 deletion src/projects.json

Large diffs are not rendered by default.

Loading