tarakan/lib/tarakan_web/live/explore_live.html.heex
Maxfield Luke af6077b9c3
All checks were successful
CI and deploy / Test (push) Successful in 4m52s
CI and deploy / Deploy production (push) Successful in 23s
Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at
https://git.elektrine.com/elektrine/tarakan.
2026-07-29 04:43:40 -04:00

263 lines
11 KiB
Text

<Layouts.app flash={@flash} current_scope={@current_scope} current_path={assigns[:current_path]}>
<Layouts.page>
<.breadcrumbs id="explore-breadcrumb">
<:crumb navigate={~p"/"}>registry</:crumb>
<:crumb>explore</:crumb>
</.breadcrumbs>
<div class="flex flex-col gap-3 border-b-2 border-strong pb-5 sm:flex-row sm:items-end sm:justify-between">
<div>
<h1 class="font-display text-3xl font-medium uppercase tracking-[0.02em] text-ink sm:text-4xl">
Explore
</h1>
<p class="mt-2 max-w-2xl text-sm leading-6 text-ink-muted">
Reports, Checks, registrations, and discussion as they hit the record.
</p>
</div>
<div class="flex flex-col items-start gap-2 sm:items-end">
<span
id="explore-watchers"
class="flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint"
>
<span class="size-1.5 rounded-full bg-phosphor"></span>
<span><span class="text-phosphor">{@watcher_count}</span> watching</span>
</span>
<div
id="explore-filter"
class="flex max-w-full overflow-x-auto overscroll-x-contain border border-strong font-display text-[10px] uppercase tracking-[0.12em] [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
role="group"
aria-label="Filter by kind"
>
<button
:for={
{value, label} <- [
{"all", "Everything"},
{"registrations", "Registrations"},
{"reports", "Reports"},
{"checks", "Checks"},
{"comments", "Comments"}
]
}
type="button"
phx-click="filter"
phx-value-kind={value}
aria-pressed={to_string(filter_active?(@kind, value))}
class={[
"shrink-0 px-3 py-2 transition sm:py-1.5",
filter_active?(@kind, value) && "bg-btn text-btn-fg",
!filter_active?(@kind, value) && "text-ink-faint hover:text-ink"
]}
>
{label}
</button>
</div>
</div>
</div>
<div class="mt-6 grid gap-10 lg:grid-cols-[minmax(0,1fr)_minmax(15rem,0.32fr)]">
<section class="min-w-0">
<form id="explore-search-form" phx-change="search" phx-submit="search">
<label
for="explore-search-input"
class="group flex cursor-text items-center gap-3 border-b border-rule px-3 sm:px-4"
>
<span
aria-hidden="true"
class="select-none font-mono text-sm text-ink-faint transition group-focus-within:text-phosphor"
>
&gt;
</span>
<input
id="explore-search-input"
type="text"
name="q"
value={@query}
autocomplete="off"
placeholder="search the wire: handle or repository"
phx-debounce="200"
phx-hook="SearchShortcut"
class="h-12 w-full border-0 bg-transparent font-mono text-xs text-ink outline-none placeholder:text-ink-faint focus:ring-0"
/>
<kbd class="hidden shrink-0 border border-rule px-1.5 py-0.5 font-mono text-[10px] text-ink-faint sm:block">
/
</kbd>
</label>
</form>
<div id="activity-wire" phx-update="stream" class="divide-y divide-rule font-mono text-xs">
<div
id="activity-wire-empty"
class="hidden px-3 py-16 text-center text-ink-faint only:block sm:px-4"
>
Nothing on the record matches this view yet.
</div>
<article
:for={{id, entry} <- @streams.wire}
id={id}
class="flex items-baseline gap-x-3 px-3 py-3 transition-colors hover:bg-panel sm:px-4"
>
<span class="shrink-0 text-[10px] tabular-nums text-ink-faint">
{ledger_time(entry.at)}
</span>
<span :if={entry.kind == :registration} class="min-w-0 truncate text-ink-muted">
<span class="text-ink-faint">registered</span>
<.link
navigate={wire_entry_path(entry)}
class="transition hover:text-ink hover:underline"
>{entry.host}/{entry.owner}/{entry.name}</.link>
</span>
<span :if={entry.kind == :scan} class="min-w-0 truncate text-ink-muted">
<.handle_link handle={entry.handle} class="font-semibold" />
<span class="text-ink-faint">reported on</span>
<.link
navigate={wire_entry_path(entry)}
class="transition hover:text-ink hover:underline"
>{entry.owner}/{entry.name}</.link>
<span class="text-ink-faint">
{String.downcase(provenance_label(entry.provenance))} · {String.slice(
entry.commit_sha,
0,
7
)}
</span>
</span>
<span :if={entry.kind == :verdict} class="min-w-0 truncate text-ink-muted">
<.handle_link handle={entry.handle} class="font-semibold" />
<span class="text-ink-faint">{entry.verdict} a finding on</span>
<.link
navigate={wire_entry_path(entry)}
class="transition hover:text-ink hover:underline"
>{entry.owner}/{entry.name}</.link>
</span>
<span :if={entry.kind == :comment} class="min-w-0 truncate text-ink-muted">
<.handle_link handle={entry.handle} class="font-semibold" />
<span class="text-ink-faint">commented on</span>
<.link
navigate={wire_entry_path(entry)}
class="transition hover:text-ink hover:underline"
>{entry.finding_title || "a finding"}</.link>
<span class="text-ink-faint">in {entry.owner}/{entry.name}</span>
</span>
<span class="ml-auto shrink-0">
<span :if={entry.kind == :registration} class="text-[10px] text-ink-faint">
<span :if={entry.language}>{entry.language} · </span>★ {compact_stars(
entry.stars_count
)}
</span>
<.link :if={entry.kind == :scan} navigate={wire_entry_path(entry)}>
<.notch_badge
:if={entry.findings_count == 0 and entry.review_kind == "code_review"}
class="text-ink-muted"
>
No findings reported
</.notch_badge>
<.notch_badge
:if={entry.findings_count == 0 and entry.review_kind != "code_review"}
class="text-ink-muted"
>
Reported
</.notch_badge>
<.notch_badge :if={entry.findings_count > 0} class="text-signal">
{entry.findings_count}
{if entry.findings_count == 1, do: "finding", else: "findings"}
</.notch_badge>
</.link>
<.link :if={entry.kind == :verdict} navigate={wire_entry_path(entry)}>
<.notch_badge
:if={entry.verdict == "confirmed" and entry.scan_verified}
class="bg-ink text-ground"
>
Verified
</.notch_badge>
<.notch_badge :if={entry.verdict == "disputed"} class="text-signal">
Disputed
</.notch_badge>
</.link>
</span>
</article>
</div>
</section>
<aside class="space-y-10">
<div id="hot-infestations">
<div class="flex items-baseline justify-between gap-3 border-b-2 border-strong px-3 pb-3">
<h2 class="font-display text-sm uppercase tracking-[0.12em] text-ink">
Infestations
</h2>
<.link
navigate={~p"/infestations"}
class="font-mono text-[10px] uppercase tracking-[0.12em] text-signal hover:underline"
>
All →
</.link>
</div>
<p :if={@infestations == []} class="px-3 py-6 font-mono text-xs text-ink-faint">
No infestations this month.
</p>
<ol :if={@infestations != []} class="divide-y divide-rule">
<li :for={infestation <- @infestations}>
<.link
id={"hot-infestation-#{infestation.pattern_key}"}
navigate={~p"/infestations/#{infestation.pattern_key}"}
class="group flex items-baseline gap-4 px-3 py-4 transition-colors hover:bg-panel"
>
<span class="shrink-0 font-mono text-xs tabular-nums text-signal">
{infestation.repo_count}
</span>
<span class="min-w-0">
<span class="block truncate text-sm font-semibold leading-5 text-ink transition group-hover:underline">
{infestation.title}
</span>
<span class="mt-1.5 block font-mono text-[11px] text-ink-faint">
{infestation.repo_count} repos · {infestation.open_count} open
</span>
</span>
</.link>
</li>
</ol>
</div>
<div id="hot-findings">
<div class="flex items-baseline justify-between gap-3 border-b-2 border-strong px-3 pb-3">
<h2 class="font-display text-sm uppercase tracking-[0.12em] text-ink">
Hot findings
</h2>
<span class="font-mono text-[11px] text-ink-faint">
Last 7 days
</span>
</div>
<p :if={@hot_findings == []} class="px-3 py-8 font-mono text-xs text-ink-faint">
No findings upvoted this week.
</p>
<ol :if={@hot_findings != []} class="divide-y divide-rule">
<li :for={finding <- @hot_findings}>
<.link
id={"hot-finding-#{finding.id}"}
navigate={~p"/findings/#{finding.public_id}"}
class="group flex items-baseline gap-4 px-3 py-4 transition-colors hover:bg-panel"
>
<span class="shrink-0 font-mono text-xs tabular-nums text-phosphor">
+{finding.score}
</span>
<span class="min-w-0">
<span class="block truncate text-sm font-semibold leading-5 text-ink transition group-hover:underline">
{finding.title}
</span>
<span class="mt-1.5 block truncate font-mono text-[11px] text-ink-faint">
{finding.owner}/{finding.name}
<span :if={finding.severity}> · {finding.severity}</span>
</span>
</span>
</.link>
</li>
</ol>
</div>
</aside>
</div>
</Layouts.page>
</Layouts.app>