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
354
lib/tarakan_web/live/account_live/profile.html.heex
Normal file
354
lib/tarakan_web/live/account_live/profile.html.heex
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
<Layouts.app flash={@flash} current_scope={@current_scope} current_path={assigns[:current_path]}>
|
||||
<Layouts.page>
|
||||
<.breadcrumbs id="profile-breadcrumb">
|
||||
<:crumb navigate={~p"/"}>registry</:crumb>
|
||||
<:crumb>@{@account.handle}</:crumb>
|
||||
</.breadcrumbs>
|
||||
|
||||
<header id="profile-header" class="border-b-2 border-strong pb-6">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<.notch_badge
|
||||
:if={role_label(@account.platform_role)}
|
||||
id="profile-role"
|
||||
class="text-signal"
|
||||
>
|
||||
{role_label(@account.platform_role)}
|
||||
</.notch_badge>
|
||||
<.notch_badge id="profile-tier" class="text-ink-muted">
|
||||
{tier_label(@account.trust_tier)}
|
||||
</.notch_badge>
|
||||
<span :if={@badges != []} id="profile-badges" class="contents">
|
||||
<.notch_badge
|
||||
:for={badge <- @badges}
|
||||
id={"profile-badge-#{badge}"}
|
||||
class="text-quote"
|
||||
>
|
||||
{badge_label(badge)}
|
||||
</.notch_badge>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
id="profile-handle"
|
||||
class="mt-3 break-words font-display text-3xl font-medium uppercase leading-tight tracking-[0.02em] text-ink sm:text-4xl"
|
||||
>
|
||||
<span class="text-ink-faint">@</span>{@account.handle}
|
||||
</h1>
|
||||
<p :if={@account.display_name} class="mt-1 text-sm text-ink-muted">
|
||||
{@account.display_name}
|
||||
</p>
|
||||
|
||||
<div class="mt-4 flex flex-wrap items-center gap-x-5 gap-y-2 font-mono text-xs text-ink-faint">
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5"
|
||||
title={"#{@reputation.verification} from verified work · #{@reputation.votes} from votes"}
|
||||
>
|
||||
<.icon name="hero-bolt" class="size-3.5" />
|
||||
<span id="profile-reputation" class="tabular-nums text-ink-muted">
|
||||
{@reputation.total}
|
||||
</span>
|
||||
reputation
|
||||
<span class="text-ink-faint">
|
||||
({@reputation.verification} verified · {@reputation.votes} votes<span :if={
|
||||
@reputation.at_risk > 0
|
||||
}> · {@reputation.at_risk} at stake in {@stake_summary.at_risk} {if(
|
||||
@stake_summary.at_risk == 1,
|
||||
do: "report",
|
||||
else: "reports"
|
||||
)}</span><span
|
||||
:if={@reputation.slashed > 0}
|
||||
class="text-signal"
|
||||
> · {@reputation.slashed} slashed across {@stake_summary.slashed} {if(
|
||||
@stake_summary.slashed == 1,
|
||||
do: "report",
|
||||
else: "reports"
|
||||
)}</span>)
|
||||
</span>
|
||||
</span>
|
||||
<span class="inline-flex items-center gap-1.5">
|
||||
<.icon name="hero-clock" class="size-3.5" /> joined {joined_on(@account.inserted_at)}
|
||||
</span>
|
||||
<a
|
||||
:for={identity <- @account.identities}
|
||||
:if={identity.profile_url}
|
||||
id={"profile-identity-#{identity.provider}"}
|
||||
href={identity.profile_url}
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
class="inline-flex items-center gap-1.5 transition hover:text-signal"
|
||||
>
|
||||
<.icon name="hero-arrow-up-right" class="size-3.5" />
|
||||
{provider_label(identity.provider)}<span :if={identity.provider_login}>/{identity.provider_login}</span>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section
|
||||
id="profile-stats"
|
||||
class="mt-8 grid grid-cols-2 border-2 border-strong lg:grid-cols-5"
|
||||
>
|
||||
<div class="border-b border-r border-rule px-6 py-7 lg:border-b-0">
|
||||
<p class="text-xs font-medium text-ink-faint">Reports</p>
|
||||
<p
|
||||
id="profile-review-count"
|
||||
class="mt-3 font-display text-4xl font-medium tabular-nums text-ink"
|
||||
>
|
||||
{@stats.reviews}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-b border-rule px-6 py-7 lg:border-b-0 lg:border-r">
|
||||
<p class="text-xs font-medium text-ink-faint">Findings</p>
|
||||
<p
|
||||
id="profile-finding-count"
|
||||
class="mt-3 font-display text-4xl font-medium tabular-nums text-signal"
|
||||
>
|
||||
{@stats.findings}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-b border-r border-rule px-6 py-7 lg:border-b-0">
|
||||
<p class="text-xs font-medium text-ink-faint">Checks</p>
|
||||
<p
|
||||
id="profile-verdict-count"
|
||||
class="mt-3 font-display text-4xl font-medium tabular-nums text-ink"
|
||||
>
|
||||
{@stats.verdicts}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-b border-rule px-6 py-7 lg:border-b-0 lg:border-r">
|
||||
<p class="text-xs font-medium text-ink-faint">
|
||||
Repositories
|
||||
</p>
|
||||
<p
|
||||
id="profile-repo-count"
|
||||
class="mt-3 font-display text-4xl font-medium tabular-nums text-ink"
|
||||
>
|
||||
{@stats.repositories}
|
||||
</p>
|
||||
</div>
|
||||
<div class="px-6 py-7">
|
||||
<p class="text-xs font-medium text-ink-faint">Credits</p>
|
||||
<p
|
||||
id="profile-credit-balance"
|
||||
class="mt-3 font-display text-4xl font-medium tabular-nums text-ink"
|
||||
>
|
||||
{@credit_balance}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="mt-10 grid gap-10 lg:grid-cols-2">
|
||||
<section id="profile-repositories">
|
||||
<h2 class="font-display text-lg uppercase tracking-[0.02em] text-ink">
|
||||
Repositories added
|
||||
</h2>
|
||||
<p class="mt-1 font-mono text-[11px] text-ink-faint">
|
||||
Publicly listed repositories this contributor registered.
|
||||
</p>
|
||||
|
||||
<p
|
||||
:if={@repositories == []}
|
||||
id="profile-no-repositories"
|
||||
class="mt-4 font-mono text-xs text-ink-faint"
|
||||
>
|
||||
No listed repositories yet.
|
||||
</p>
|
||||
|
||||
<ul
|
||||
:if={@repositories != []}
|
||||
class="mt-4 divide-y divide-rule border-t border-rule"
|
||||
>
|
||||
<li :for={repository <- @repositories} id={"profile-repo-#{repository.id}"}>
|
||||
<.link
|
||||
navigate={TarakanWeb.RepositoryPaths.repository_path(repository)}
|
||||
class="group flex items-baseline gap-x-3 px-3 py-3 font-mono text-xs transition-colors hover:bg-panel"
|
||||
>
|
||||
<span class="shrink-0 tabular-nums text-ink-faint">
|
||||
{activity_time(repository.inserted_at)}
|
||||
</span>
|
||||
<span class="min-w-0 truncate text-ink-muted transition group-hover:text-ink">
|
||||
<span class="text-ink-faint">{repository.host}/</span>{repository.owner}/{repository.name}
|
||||
</span>
|
||||
<span class="ml-auto shrink-0 text-[10px] text-ink-faint">
|
||||
<span :if={repository.primary_language}>{repository.primary_language}</span>
|
||||
</span>
|
||||
</.link>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="profile-reviews">
|
||||
<h2 class="font-display text-lg uppercase tracking-[0.02em] text-ink">
|
||||
Reports submitted
|
||||
</h2>
|
||||
<p class="mt-1 font-mono text-[11px] text-ink-faint">
|
||||
Security reviews pinned to exact commits.
|
||||
</p>
|
||||
|
||||
<p
|
||||
:if={@reviews == []}
|
||||
id="profile-no-reviews"
|
||||
class="mt-4 font-mono text-xs text-ink-faint"
|
||||
>
|
||||
No public reports yet.
|
||||
</p>
|
||||
|
||||
<ul :if={@reviews != []} class="mt-4 divide-y divide-rule border-t border-rule">
|
||||
<li :for={entry <- @reviews} id={"profile-review-#{entry.id}"}>
|
||||
<.link
|
||||
navigate={TarakanWeb.RepositoryPaths.repository_security_path(entry.repository)}
|
||||
class="group flex items-baseline gap-x-3 px-3 py-3 font-mono text-xs transition-colors hover:bg-panel"
|
||||
>
|
||||
<span class="shrink-0 tabular-nums text-ink-faint">{activity_time(entry.at)}</span>
|
||||
<span class="min-w-0 truncate text-ink-muted transition group-hover:text-ink">
|
||||
<span class="text-ink-faint">reviewed</span>
|
||||
{entry.repository.owner}/{entry.repository.name}
|
||||
<span :if={entry.commit_sha} class="text-ink-faint">
|
||||
· {short_sha(entry.commit_sha)}
|
||||
</span>
|
||||
</span>
|
||||
<span class="ml-auto shrink-0 text-[10px] text-ink-faint">
|
||||
<span :if={entry.verified} class="text-ink-muted">verified · </span>
|
||||
<span :if={entry.findings_count > 0} class="text-signal">
|
||||
{entry.findings_count} findings
|
||||
</span>
|
||||
<span :if={entry.findings_count == 0}>no findings</span>
|
||||
</span>
|
||||
</.link>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="profile-findings">
|
||||
<h2 class="font-display text-lg uppercase tracking-[0.02em] text-ink">
|
||||
Findings filed
|
||||
</h2>
|
||||
<p class="mt-1 font-mono text-[11px] text-ink-faint">
|
||||
Individual findings from public reports.
|
||||
</p>
|
||||
|
||||
<p
|
||||
:if={@findings == []}
|
||||
id="profile-no-findings"
|
||||
class="mt-4 font-mono text-xs text-ink-faint"
|
||||
>
|
||||
No public findings yet.
|
||||
</p>
|
||||
|
||||
<ul :if={@findings != []} class="mt-4 divide-y divide-rule border-t border-rule">
|
||||
<li :for={entry <- @findings} id={"profile-finding-#{entry.public_id}"}>
|
||||
<.link
|
||||
navigate={~p"/findings/#{entry.public_id}"}
|
||||
class="group flex items-baseline gap-x-3 px-3 py-3 font-mono text-xs transition-colors hover:bg-panel"
|
||||
>
|
||||
<span class="shrink-0 tabular-nums text-ink-faint">{activity_time(entry.at)}</span>
|
||||
<span class="min-w-0 truncate text-ink-muted transition group-hover:text-ink">
|
||||
<span class={[
|
||||
"mr-1.5 inline-block border px-1 text-[10px] uppercase tracking-[0.12em]",
|
||||
entry.severity in ~w(critical high) && "border-signal text-signal",
|
||||
entry.severity not in ~w(critical high) && "border-rule text-ink-faint"
|
||||
]}>
|
||||
{entry.severity}
|
||||
</span>
|
||||
{entry.title}
|
||||
</span>
|
||||
<span class="ml-auto shrink-0 text-[10px] text-ink-faint">
|
||||
{entry.repository.owner}/{entry.repository.name}
|
||||
</span>
|
||||
</.link>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="profile-checks">
|
||||
<h2 class="font-display text-lg uppercase tracking-[0.02em] text-ink">
|
||||
Checks cast
|
||||
</h2>
|
||||
<p class="mt-1 font-mono text-[11px] text-ink-faint">
|
||||
Independent confirmations, disputes, and fix notes.
|
||||
</p>
|
||||
|
||||
<p
|
||||
:if={@checks == []}
|
||||
id="profile-no-checks"
|
||||
class="mt-4 font-mono text-xs text-ink-faint"
|
||||
>
|
||||
No public checks yet.
|
||||
</p>
|
||||
|
||||
<ul :if={@checks != []} class="mt-4 divide-y divide-rule border-t border-rule">
|
||||
<li :for={entry <- @checks} id={"profile-check-#{entry.id}"}>
|
||||
<.link
|
||||
:if={entry.public_id || entry.repository}
|
||||
navigate={check_path(entry)}
|
||||
class="group flex items-baseline gap-x-3 px-3 py-3 font-mono text-xs transition-colors hover:bg-panel"
|
||||
>
|
||||
<span class="shrink-0 tabular-nums text-ink-faint">{activity_time(entry.at)}</span>
|
||||
<span class="min-w-0 truncate text-ink-muted transition group-hover:text-ink">
|
||||
<span class={[
|
||||
entry.verdict == "confirmed" && "text-quote",
|
||||
entry.verdict == "disputed" && "text-signal",
|
||||
entry.verdict == "fixed" && "text-ink"
|
||||
]}>
|
||||
{entry.verdict}
|
||||
</span>
|
||||
<span :if={entry.title} class="text-ink-faint"> · </span>
|
||||
<span :if={entry.title}>{entry.title}</span>
|
||||
<span
|
||||
:if={is_nil(entry.title) and entry.repository}
|
||||
class="text-ink-faint"
|
||||
>
|
||||
on {entry.repository.owner}/{entry.repository.name}
|
||||
</span>
|
||||
</span>
|
||||
<span class="ml-auto shrink-0 text-[10px] text-ink-faint">
|
||||
{provenance_label(entry.provenance)}
|
||||
</span>
|
||||
</.link>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section id="profile-credits" class="mt-10">
|
||||
<h2 class="font-display text-lg uppercase tracking-[0.02em] text-ink">
|
||||
Credits
|
||||
</h2>
|
||||
<p class="mt-1 font-mono text-[11px] text-ink-faint">
|
||||
Earned on verification events only - never purchasable, never transferable.
|
||||
</p>
|
||||
|
||||
<p
|
||||
:if={@credit_entries == []}
|
||||
id="profile-no-credits"
|
||||
class="mt-4 font-mono text-xs text-ink-faint"
|
||||
>
|
||||
No credit activity yet.
|
||||
</p>
|
||||
|
||||
<ul :if={@credit_entries != []} class="mt-4 divide-y divide-rule border-t border-rule">
|
||||
<li
|
||||
:for={entry <- @credit_entries}
|
||||
id={"profile-credit-#{entry.id}"}
|
||||
class="flex items-baseline gap-x-3 px-3 py-3 font-mono text-xs"
|
||||
>
|
||||
<span class="shrink-0 tabular-nums text-ink-faint">
|
||||
{activity_time(entry.inserted_at)}
|
||||
</span>
|
||||
<span class="min-w-0 truncate text-ink-muted">
|
||||
{credit_kind_label(entry.kind)}
|
||||
</span>
|
||||
<span class={[
|
||||
"ml-auto shrink-0 tabular-nums",
|
||||
entry.amount > 0 && "text-quote",
|
||||
entry.amount < 0 && "text-signal"
|
||||
]}>
|
||||
{credit_amount(entry.amount)}
|
||||
</span>
|
||||
<span class="shrink-0 tabular-nums text-ink-faint">
|
||||
balance {entry.balance_after}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</Layouts.page>
|
||||
</Layouts.app>
|
||||
Loading…
Add table
Add a link
Reference in a new issue