Files
iknowyou/front/src/components/HomePage.svelte
2026-04-11 20:43:30 +02:00

111 lines
3.4 KiB
Svelte

<script>
import { RotateCw, AlertTriangle } from "@lucide/svelte";
import SearchBar from "./SearchBar.svelte";
import SearchList from "./SearchList.svelte";
import { onMount } from "svelte";
let searches = $state([]);
let loadError = $state("");
let redirecting = $state(false);
let redirectTarget = $state("");
let demo = $state(false);
let prefill = $state({ target: "", type: "" });
let ready = $state(false);
onMount(async () => {
loadSearches();
fetch("/api/config")
.then((r) => r.ok ? r.json() : null)
.then((d) => { if (d) demo = d.demo === true; })
.catch(() => {});
const params = new URLSearchParams(window.location.search);
const target = params.get("target");
const type = params.get("type");
const fillOnly = params.get("fillOnly") === "true";
if (target && type) {
// Clean URL before acting so a refresh doesn't re-trigger
window.history.replaceState({}, "", window.location.pathname);
if (fillOnly) {
prefill = { target, type };
ready = true;
return;
}
ready = true;
await handleSearch(target, type, params.get("profile") || "default");
} else {
ready = true;
}
});
async function loadSearches() {
try {
const res = await fetch("/api/searches");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
searches = (data ?? []).sort(
(a, b) => new Date(b.started_at) - new Date(a.started_at)
);
} catch (e) {
loadError = e.message;
}
}
async function handleSearch(target, inputType, profile) {
redirectTarget = target;
redirecting = true;
try {
const res = await fetch("/api/searches", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ target, input_type: inputType, profile: profile || undefined }),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error || `HTTP ${res.status}`);
}
const s = await res.json();
window.location.href = `/search/${s.id}`;
} catch (e) {
redirecting = false;
throw e;
}
}
async function handleDelete(id) {
await fetch(`/api/searches/${id}`, { method: "DELETE" });
searches = searches.filter((s) => s.id !== id);
}
</script>
<div class="flex flex-col gap-8">
<div class="card bg-base-200 shadow p-6">
{#if redirecting}
<div class="flex flex-col items-center justify-center gap-3 py-4">
<span class="loading loading-dots loading-md text-primary"></span>
<p class="text-sm text-base-content/60">
Searching <span class="font-mono text-base-content/90">{redirectTarget}</span>...
</p>
</div>
{:else if ready}
<SearchBar onSearch={handleSearch} {demo} initialTarget={prefill.target} initialType={prefill.type} />
{/if}
</div>
<div class="flex flex-col gap-3">
<div class="flex items-center justify-between">
<h2 class="text-xs uppercase tracking-widest text-base-content/50">Recent searches</h2>
<button class="btn btn-ghost btn-xs" onclick={loadSearches}><RotateCw class="size-3" /> refresh</button>
</div>
{#if loadError}
<div class="alert alert-error text-sm gap-2"><AlertTriangle size={15} class="shrink-0" />{loadError}</div>
{:else}
<SearchList {searches} onDelete={handleDelete} />
{/if}
</div>
</div>