Skip to content

Commit 90ac4ad

Browse files
committed
add top contributions on user profiles
1 parent c4bb44b commit 90ac4ad

File tree

1 file changed

+109
-37
lines changed

1 file changed

+109
-37
lines changed

lib/algora_web/live/user/profile_live.ex

Lines changed: 109 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ defmodule AlgoraWeb.User.ProfileLive do
1414
{:ok, user} ->
1515
transactions = Payments.list_received_transactions(user.id, limit: page_size())
1616

17+
contributions = Algora.Workspace.list_user_contributions(user.provider_login, limit: 20)
18+
1719
{:ok,
1820
socket
1921
|> assign(:user, user)
2022
|> assign(:page_title, "#{user.name}")
2123
|> assign(:page_description, "Open source contributions and bounty history of #{user.name}")
2224
|> assign(:reviews, Reviews.list_reviews(reviewee_id: user.id, limit: 10))
2325
|> assign(:transactions, to_transaction_rows(transactions))
24-
|> assign(:has_more_transactions, length(transactions) >= page_size())}
26+
|> assign(:has_more_transactions, length(transactions) >= page_size())
27+
|> assign(:contributions, contributions)}
2528

2629
{:error, :not_found} ->
2730
raise AlgoraWeb.NotFoundError
@@ -188,46 +191,95 @@ defmodule AlgoraWeb.User.ProfileLive do
188191
<!-- Reviews Column -->
189192
<div class="space-y-4">
190193
<div class="grid grid-cols-1 gap-4">
191-
<h2 class="text-lg font-semibold">Completed Contracts</h2>
192-
<%= if Enum.empty?(@reviews) do %>
193-
<.card class="text-center">
194-
<.card_header>
195-
<div class="mx-auto mb-2 rounded-full bg-muted p-4">
196-
<.icon name="tabler-contract" class="h-8 w-8 text-muted-foreground" />
194+
<%= if Enum.empty?(@contributions) do %>
195+
<h2 class="text-lg font-semibold">Completed Contracts</h2>
196+
<%= if Enum.empty?(@reviews) do %>
197+
<.card class="text-center">
198+
<.card_header>
199+
<div class="mx-auto mb-2 rounded-full bg-muted p-4">
200+
<.icon name="tabler-contract" class="h-8 w-8 text-muted-foreground" />
201+
</div>
202+
<.card_title>No completed contracts yet</.card_title>
203+
<.card_description>
204+
Contracts will appear here once completed
205+
</.card_description>
206+
</.card_header>
207+
</.card>
208+
<% else %>
209+
<%= for review <- @reviews do %>
210+
<div class="w-full rounded-lg border border-border bg-card p-4 text-sm">
211+
<div class="mb-2 flex items-center gap-1">
212+
<%= for i <- 1..Review.max_rating() do %>
213+
<.icon
214+
name="tabler-star-filled"
215+
class={"#{if i <= review.rating, do: "text-foreground", else: "text-muted-foreground/25"} h-4 w-4"}
216+
/>
217+
<% end %>
218+
</div>
219+
<p class="mb-2 text-sm">{review.content}</p>
220+
<div class="flex items-center gap-3">
221+
<.avatar class="h-8 w-8">
222+
<.avatar_image src={review.reviewer.avatar_url} alt={review.reviewer.name} />
223+
<.avatar_fallback>
224+
{Algora.Util.initials(review.reviewer.name)}
225+
</.avatar_fallback>
226+
</.avatar>
227+
<div class="flex flex-col">
228+
<p class="text-sm font-medium">{review.reviewer.name}</p>
229+
<p class="text-xs text-muted-foreground">
230+
{review.organization.name}
231+
</p>
232+
</div>
233+
</div>
197234
</div>
198-
<.card_title>No completed contracts yet</.card_title>
199-
<.card_description>
200-
Contracts will appear here once completed
201-
</.card_description>
202-
</.card_header>
203-
</.card>
235+
<% end %>
236+
<% end %>
204237
<% else %>
205-
<%= for review <- @reviews do %>
206-
<div class="w-full rounded-lg border border-border bg-card p-4 text-sm">
207-
<div class="mb-2 flex items-center gap-1">
208-
<%= for i <- 1..Review.max_rating() do %>
209-
<.icon
210-
name="tabler-star-filled"
211-
class={"#{if i <= review.rating, do: "text-foreground", else: "text-muted-foreground/25"} h-4 w-4"}
212-
/>
213-
<% end %>
214-
</div>
215-
<p class="mb-2 text-sm">{review.content}</p>
216-
<div class="flex items-center gap-3">
217-
<.avatar class="h-8 w-8">
218-
<.avatar_image src={review.reviewer.avatar_url} alt={review.reviewer.name} />
219-
<.avatar_fallback>
220-
{Algora.Util.initials(review.reviewer.name)}
221-
</.avatar_fallback>
222-
</.avatar>
223-
<div class="flex flex-col">
224-
<p class="text-sm font-medium">{review.reviewer.name}</p>
225-
<p class="text-xs text-muted-foreground">
226-
{review.organization.name}
227-
</p>
238+
<h2 class="text-lg font-semibold">Top Contributions</h2>
239+
<%= for {owner, contributions} <- aggregate_contributions(@contributions) do %>
240+
<.link
241+
href={"https://github.com/#{owner.provider_login}/#{List.first(contributions).repository.name}/pulls?q=author%3A#{@user.provider_login}+is%3Amerged+"}
242+
target="_blank"
243+
rel="noopener"
244+
class="flex items-center gap-3 rounded-xl pr-2 bg-card/50 border border-border/50 hover:border-border transition-all"
245+
>
246+
<img
247+
src={owner.avatar_url}
248+
class="h-12 w-12 rounded-xl rounded-r-none transition-all"
249+
alt={owner.name}
250+
/>
251+
<div class="w-full flex flex-col text-xs font-medium gap-0.5">
252+
<span class="flex items-start justify-between gap-5">
253+
<span class="font-display">
254+
{if owner.type == :organization do
255+
owner.name
256+
else
257+
List.first(contributions).repository.name
258+
end}
259+
</span>
260+
<%= if tech = List.first(List.first(contributions).repository.tech_stack) do %>
261+
<span class="flex items-center text-foreground text-[11px] gap-1">
262+
<img
263+
src={"https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/#{String.downcase(tech)}/#{String.downcase(tech)}-original.svg"}
264+
class="w-4 h-4 invert saturate-0"
265+
/> {tech}
266+
</span>
267+
<% end %>
268+
</span>
269+
<div class="flex items-center gap-2 font-semibold">
270+
<span class="flex items-center text-amber-300 text-xs">
271+
<.icon name="tabler-star-filled" class="h-4 w-4 mr-1" />
272+
{Algora.Util.format_number_compact(
273+
max(owner.stargazers_count, total_stars(contributions))
274+
)}
275+
</span>
276+
<span class="flex items-center text-purple-400 text-xs">
277+
<.icon name="tabler-git-pull-request" class="h-4 w-4 mr-1" />
278+
{Algora.Util.format_number_compact(total_contributions(contributions))}
279+
</span>
228280
</div>
229281
</div>
230-
</div>
282+
</.link>
231283
<% end %>
232284
<% end %>
233285
</div>
@@ -276,4 +328,24 @@ defmodule AlgoraWeb.User.ProfileLive do
276328
)
277329
end)
278330
end
331+
332+
defp aggregate_contributions(contributions) do
333+
groups = Enum.group_by(contributions, fn c -> c.repository.user end)
334+
335+
contributions
336+
|> Enum.map(fn c -> {c.repository.user, groups[c.repository.user]} end)
337+
|> Enum.dedup_by(fn {owner, _} -> owner end)
338+
end
339+
340+
defp total_stars(contributions) do
341+
contributions
342+
|> Enum.map(& &1.repository.stargazers_count)
343+
|> Enum.sum()
344+
end
345+
346+
defp total_contributions(contributions) do
347+
contributions
348+
|> Enum.map(& &1.contribution_count)
349+
|> Enum.sum()
350+
end
279351
end

0 commit comments

Comments
 (0)