Initial commit on Forgejo
Fresh repository history for elektrine/tarakan hosted at https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
commit
af6077b9c3
455 changed files with 78366 additions and 0 deletions
219
lib/tarakan_web/components/infestation_components.ex
Normal file
219
lib/tarakan_web/components/infestation_components.ex
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
defmodule TarakanWeb.InfestationComponents do
|
||||
@moduledoc """
|
||||
Functional infestation surfaces: ranked pattern table and per-pattern repo matrix.
|
||||
"""
|
||||
use Phoenix.Component
|
||||
|
||||
use Phoenix.VerifiedRoutes,
|
||||
endpoint: TarakanWeb.Endpoint,
|
||||
router: TarakanWeb.Router,
|
||||
statics: TarakanWeb.static_paths()
|
||||
|
||||
alias TarakanWeb.RepositoryPaths
|
||||
|
||||
attr :infestations, :list, required: true
|
||||
attr :id, :string, default: "infestation-constellation"
|
||||
attr :compact, :boolean, default: false
|
||||
|
||||
@doc "Ranked multi-repo patterns (homepage / index)."
|
||||
def constellation(assigns) do
|
||||
max_repos =
|
||||
assigns.infestations
|
||||
|> Enum.map(& &1.repo_count)
|
||||
|> Enum.max(fn -> 1 end)
|
||||
|
||||
assigns = assign(assigns, :max_repos, max(max_repos, 1))
|
||||
|
||||
~H"""
|
||||
<div id={@id} class="border-2 border-strong bg-ground">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full min-w-[36rem] border-collapse text-left">
|
||||
<thead>
|
||||
<tr class="border-b border-rule font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint">
|
||||
<th class="w-10 px-3 py-2 font-normal sm:px-4">#</th>
|
||||
<th class="px-2 py-2 font-normal">Pattern</th>
|
||||
<th class="w-[7rem] px-2 py-2 font-normal sm:w-[10rem]">Spread</th>
|
||||
<th class="w-16 px-2 py-2 text-right font-normal tabular-nums">Repos</th>
|
||||
<th class="hidden w-14 px-2 py-2 text-right font-normal sm:table-cell">Open</th>
|
||||
<th class="hidden w-14 px-2 py-2 text-right font-normal md:table-cell">Ver</th>
|
||||
<th class="hidden w-14 px-2 py-2 text-right font-normal md:table-cell">Fix</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-rule">
|
||||
<tr
|
||||
:for={{infestation, i} <- Enum.with_index(@infestations, 1)}
|
||||
class="group transition-colors hover:bg-panel"
|
||||
>
|
||||
<td class="px-3 py-2.5 font-mono text-[11px] tabular-nums text-ink-faint sm:px-4">
|
||||
{i}
|
||||
</td>
|
||||
<td class="min-w-0 px-2 py-2.5">
|
||||
<.link
|
||||
id={"#{@id}-hub-#{infestation.pattern_key}"}
|
||||
navigate={~p"/infestations/#{infestation.pattern_key}"}
|
||||
class="block min-w-0"
|
||||
>
|
||||
<span class="flex flex-wrap items-center gap-2">
|
||||
<span
|
||||
:if={infestation.severity}
|
||||
class="shrink-0 font-mono text-[10px] uppercase tracking-[0.12em] text-signal"
|
||||
>
|
||||
{infestation.severity}
|
||||
</span>
|
||||
<span class="truncate text-sm font-semibold text-ink group-hover:text-signal">
|
||||
{infestation.title}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
:if={!@compact}
|
||||
class="mt-0.5 block truncate font-mono text-[10px] text-ink-faint"
|
||||
>
|
||||
{String.slice(infestation.pattern_key, 0, 12)}…
|
||||
</span>
|
||||
</.link>
|
||||
</td>
|
||||
<td class="px-2 py-2.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="h-1.5 min-w-0 flex-1 bg-rule">
|
||||
<div
|
||||
class="h-full bg-signal"
|
||||
style={"width: #{bar_pct(infestation.repo_count, @max_repos)}%"}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-2 py-2.5 text-right font-display text-lg tabular-nums text-ink">
|
||||
{infestation.repo_count}
|
||||
</td>
|
||||
<td class="hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums text-phosphor sm:table-cell">
|
||||
{infestation.open_count}
|
||||
</td>
|
||||
<td class="hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums text-ink md:table-cell">
|
||||
{infestation.verified_count}
|
||||
</td>
|
||||
<td class="hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums text-ink-muted md:table-cell">
|
||||
{infestation.fixed_count}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
attr :repos, :list, required: true, doc: "pattern_repos rows: one per repository"
|
||||
attr :infestation, :map, required: true
|
||||
attr :pattern_key, :string, required: true
|
||||
attr :id, :string, default: "infestation-graph"
|
||||
|
||||
@doc """
|
||||
Per-repo infestation matrix for one pattern.
|
||||
|
||||
One row per repository, rolled up from that repo's instances. The instance
|
||||
grain (one row per finding) is the separate ledger on the same page.
|
||||
"""
|
||||
def infestation_graph(assigns) do
|
||||
~H"""
|
||||
<div id={@id} class="border-2 border-strong bg-ground">
|
||||
<div
|
||||
:if={@repos == []}
|
||||
class="px-4 py-10 text-center font-mono text-xs text-ink-faint"
|
||||
>
|
||||
No listed public repositories.
|
||||
</div>
|
||||
|
||||
<div :if={@repos != []} class="overflow-x-auto">
|
||||
<table class="w-full min-w-[32rem] border-collapse text-left">
|
||||
<thead>
|
||||
<tr class="border-b border-rule font-mono text-[10px] uppercase tracking-[0.12em] text-ink-faint">
|
||||
<th class="w-8 px-3 py-2 font-normal sm:px-4"></th>
|
||||
<th class="px-2 py-2 font-normal">Repository</th>
|
||||
<th class="w-24 px-2 py-2 font-normal">Status</th>
|
||||
<th class="w-20 px-2 py-2 text-right font-normal tabular-nums">Findings</th>
|
||||
<th class="hidden w-16 px-2 py-2 text-right font-normal tabular-nums sm:table-cell">
|
||||
Open
|
||||
</th>
|
||||
<th class="w-20 px-3 py-2 text-right font-normal sm:px-4"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-rule">
|
||||
<tr
|
||||
:for={repo <- @repos}
|
||||
id={"#{@id}-node-#{repo.id}"}
|
||||
class="group transition-colors hover:bg-panel"
|
||||
>
|
||||
<td class="px-3 py-2.5 sm:px-4">
|
||||
<span class={[
|
||||
"inline-block size-2 rounded-full",
|
||||
repo.status == "open" && "bg-phosphor",
|
||||
repo.status == "verified" && "bg-ink",
|
||||
repo.status == "fixed" && "bg-ink-muted",
|
||||
repo.status not in ["open", "verified", "fixed"] && "bg-ink-faint"
|
||||
]}></span>
|
||||
</td>
|
||||
<td class="min-w-0 px-2 py-2.5">
|
||||
<.link
|
||||
:if={repo[:host]}
|
||||
navigate={RepositoryPaths.repository_path(repo)}
|
||||
class="block truncate font-mono text-xs font-semibold text-ink hover:text-signal hover:underline"
|
||||
>
|
||||
{repo.owner}/{repo.name}
|
||||
</.link>
|
||||
<span
|
||||
:if={is_nil(repo[:host])}
|
||||
class="block truncate font-mono text-xs font-semibold text-ink"
|
||||
>
|
||||
{repo.owner}/{repo.name}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-2 py-2.5">
|
||||
<span class={[
|
||||
"font-mono text-[10px] uppercase tracking-[0.12em]",
|
||||
repo.status == "open" && "text-phosphor",
|
||||
repo.status == "verified" && "text-ink",
|
||||
repo.status == "fixed" && "text-ink-muted",
|
||||
repo.status not in ["open", "verified", "fixed"] && "text-ink-faint"
|
||||
]}>
|
||||
{repo.status}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-2 py-2.5 text-right font-mono text-xs tabular-nums text-ink">
|
||||
{repo[:instance_count] || 1}
|
||||
</td>
|
||||
<td class={[
|
||||
"hidden px-2 py-2.5 text-right font-mono text-xs tabular-nums sm:table-cell",
|
||||
(repo[:open_count] || 0) > 0 && "text-phosphor",
|
||||
(repo[:open_count] || 0) == 0 && "text-ink-faint"
|
||||
]}>
|
||||
{repo[:open_count] || 0}
|
||||
</td>
|
||||
<td class="px-3 py-2.5 text-right sm:px-4">
|
||||
<.link
|
||||
:if={repo.occurrence_public_id}
|
||||
navigate={~p"/findings/#{repo.occurrence_public_id}"}
|
||||
class="font-mono text-[11px] text-signal hover:underline"
|
||||
>
|
||||
Finding →
|
||||
</.link>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
defp bar_pct(count, max) when max > 0 do
|
||||
count
|
||||
|> Kernel./(max)
|
||||
|> Kernel.*(100)
|
||||
|> Float.round(1)
|
||||
|> max(4)
|
||||
|> min(100)
|
||||
end
|
||||
|
||||
defp bar_pct(_, _), do: 0
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue