146 lines
3.9 KiB
Svelte
146 lines
3.9 KiB
Svelte
<script lang="ts">
|
|
import type { Dataleak } from "$src/lib/types";
|
|
import { Database, Replace, Search } from "@lucide/svelte";
|
|
|
|
let {
|
|
dataleaks,
|
|
perPage = 5,
|
|
showColumns = false,
|
|
}: {
|
|
dataleaks: Dataleak[];
|
|
perPage?: number;
|
|
showColumns?: boolean;
|
|
} = $props();
|
|
|
|
let page = $state(1);
|
|
let filter = $state("");
|
|
let filteredDataleaks = $state<Dataleak[]>(dataleaks);
|
|
let paginatedDataleaks = $state<Dataleak[]>([]);
|
|
let totalPages = $state(0);
|
|
|
|
$effect(() => {
|
|
if (filter.trim() === "") {
|
|
filteredDataleaks = dataleaks;
|
|
} else {
|
|
const lowerFilter = filter.toLowerCase();
|
|
filteredDataleaks = dataleaks.filter((item) =>
|
|
item.Name.toLowerCase().includes(lowerFilter),
|
|
);
|
|
}
|
|
page = 1;
|
|
});
|
|
|
|
$effect(() => {
|
|
if (filteredDataleaks) {
|
|
totalPages = Math.ceil(filteredDataleaks.length / perPage);
|
|
const start = (page - 1) * perPage;
|
|
const end = start + perPage;
|
|
paginatedDataleaks = filteredDataleaks.slice(start, end);
|
|
if (page > totalPages) {
|
|
page = totalPages > 0 ? totalPages : 1;
|
|
}
|
|
}
|
|
});
|
|
|
|
function getDomain(dataleakName: string) {
|
|
const firstPart = dataleakName.split(" ")[0].toLowerCase();
|
|
const domainRegex =
|
|
/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/;
|
|
if (domainRegex.test(firstPart)) {
|
|
return firstPart;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function previousPage() {
|
|
if (page > 1) {
|
|
page--;
|
|
}
|
|
}
|
|
|
|
function nextPage() {
|
|
if (page < totalPages) {
|
|
page++;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="my-4 flex flex-col gap-2">
|
|
<label class="input input-xs w-full">
|
|
<Search size={12} />
|
|
<input class="grow" placeholder="Filter" bind:value={filter} />
|
|
</label>
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="table">
|
|
<!-- head -->
|
|
<thead>
|
|
<tr>
|
|
<th></th>
|
|
<th>Name</th>
|
|
<th>Number of rows</th>
|
|
{#if showColumns}
|
|
<th>Columns</th>
|
|
{/if}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{#if paginatedDataleaks.length > 0}
|
|
{#each paginatedDataleaks as item}
|
|
<tr class="hover:bg-base-300">
|
|
<th>
|
|
<div>
|
|
{#if getDomain(item.Name)}
|
|
<img
|
|
src="https://icons.duckduckgo.com/ip3/{getDomain(
|
|
item.Name,
|
|
)}.ico"
|
|
class="size-8 rounded-xl bg-neutral"
|
|
alt="Favicon de {getDomain(item.Name)}"
|
|
/>
|
|
{:else}
|
|
<div
|
|
class="size-8 rounded-xl bg-neutral items-center justify-center flex"
|
|
>
|
|
<Database class="text-neutral-content" />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</th>
|
|
<th>
|
|
{item.Name}
|
|
</th>
|
|
<td>{item.Length.toLocaleString("fr")}</td>
|
|
{#if showColumns}
|
|
<td class="capitalize">
|
|
{item.Columns.map((col) => col.replace(/_/g, " ")).join(", ")}
|
|
</td>
|
|
{/if}
|
|
</tr>
|
|
{/each}
|
|
{:else}
|
|
<tr class="hover:bg-base-300">
|
|
<td colspan={100} class="text-center leading-9"
|
|
><span class="text-3xl">(·.·)</span><br />No data wells found</td
|
|
>
|
|
</tr>
|
|
{/if}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{#if totalPages > 1}
|
|
<div class="join m-auto mt-5">
|
|
<button class="join-item btn" onclick={previousPage} disabled={page === 1}
|
|
>«</button
|
|
>
|
|
<button class="join-item btn">Page {page} / {totalPages}</button>
|
|
<button
|
|
class="join-item btn"
|
|
onclick={nextPage}
|
|
disabled={page === totalPages}>»</button
|
|
>
|
|
</div>
|
|
{/if}
|
|
</div>
|