Skip to content

Commit 7b46e3d

Browse files
committed
feat: display claims on bounties page
1 parent 35ea409 commit 7b46e3d

File tree

3 files changed

+87
-51
lines changed

3 files changed

+87
-51
lines changed

lib/algora/bounties/bounties.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,18 @@ defmodule Algora.Bounties do
741741
|> Repo.all()
742742
end
743743

744+
@spec list_claims(list(String.t())) :: [Claim.t()]
745+
def list_claims(ticket_ids) do
746+
Repo.all(
747+
from c in Claim,
748+
join: t in assoc(c, :target),
749+
join: user in assoc(c, :user),
750+
left_join: s in assoc(c, :source),
751+
where: t.id in ^ticket_ids,
752+
select_merge: %{user: user, source: s}
753+
)
754+
end
755+
744756
def awarded_to_user(user_id) do
745757
from b in Bounty,
746758
join: t in Transaction,

lib/algora/shared/util.ex

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,28 @@ defmodule Algora.Util do
2929
diff = NaiveDateTime.diff(now, datetime, :second)
3030

3131
cond do
32-
diff < 60 -> "just now"
33-
diff < 3600 -> "#{div(diff, 60)} minutes ago"
34-
diff < 86_400 -> "#{div(diff, 3600)} hours ago"
35-
diff < 2_592_000 -> "#{div(diff, 86_400)} days ago"
36-
true -> "#{div(diff, 2_592_000)} months ago"
32+
diff < 60 ->
33+
"just now"
34+
35+
diff < 3600 ->
36+
count = div(diff, 60)
37+
unit = Gettext.ngettext(AlgoraWeb.Gettext, "minute", "minutes", count)
38+
"#{count} #{unit} ago"
39+
40+
diff < 86_400 ->
41+
count = div(diff, 3600)
42+
unit = Gettext.ngettext(AlgoraWeb.Gettext, "hour", "hours", count)
43+
"#{count} #{unit} ago"
44+
45+
diff < 2_592_000 ->
46+
count = div(diff, 86_400)
47+
unit = Gettext.ngettext(AlgoraWeb.Gettext, "day", "days", count)
48+
"#{count} #{unit} ago"
49+
50+
true ->
51+
count = div(diff, 2_592_000)
52+
unit = Gettext.ngettext(AlgoraWeb.Gettext, "month", "months", count)
53+
"#{count} #{unit} ago"
3754
end
3855
end
3956

lib/algora_web/live/org/bounties_live.ex

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,12 @@ defmodule AlgoraWeb.Org.BountiesLive do
22
@moduledoc false
33
use AlgoraWeb, :live_view
44

5+
alias Algora.Accounts.User
56
alias Algora.Bounties
67
alias Algora.Bounties.Bounty
78

89
def mount(_params, _session, socket) do
9-
open_bounties = Bounties.list_bounties(owner_id: socket.assigns.current_org.id, limit: 10, status: :open)
10-
paid_bounties = Bounties.list_bounties(owner_id: socket.assigns.current_org.id, limit: 10, status: :paid)
11-
12-
# TODO:
13-
claims = []
14-
15-
{:ok,
16-
socket
17-
|> assign(:open_bounties, open_bounties)
18-
|> assign(:paid_bounties, paid_bounties)
19-
|> assign(:claims, claims)
20-
|> assign(:open_count, length(open_bounties))
21-
|> assign(:completed_count, length(paid_bounties))}
10+
{:ok, socket}
2211
end
2312

2413
def render(assigns) do
@@ -88,7 +77,7 @@ defmodule AlgoraWeb.Org.BountiesLive do
8877
<div class="scrollbar-thin w-full overflow-auto">
8978
<table class="w-full caption-bottom text-sm">
9079
<tbody class="[&_tr:last-child]:border-0">
91-
<%= for bounty <- (if @current_tab == :open, do: @open_bounties, else: @paid_bounties) do %>
80+
<%= for %{bounty: bounty, claims: claims} <- @bounties do %>
9281
<tr
9382
class="bg-white/[2%] from-white/[2%] via-white/[2%] to-white/[2%] border-b border-white/15 bg-gradient-to-br transition-colors data-[state=selected]:bg-gray-100 hover:bg-gray-100/50 dark:data-[state=selected]:bg-gray-800 dark:hover:bg-white/[2%]"
9483
data-state="false"
@@ -128,13 +117,13 @@ defmodule AlgoraWeb.Org.BountiesLive do
128117
</div>
129118
</td>
130119
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle">
131-
<%= if length(@claims) > 0 do %>
120+
<%= if length(claims) > 0 do %>
132121
<div class="group flex cursor-pointer flex-col items-center gap-1">
133122
<div class="flex cursor-pointer justify-center -space-x-3">
134-
<%= for claim <- @claims do %>
123+
<%= for claim <- claims do %>
135124
<div class="relative h-10 w-10 flex-shrink-0 rounded-full ring-4 ring-gray-800 group-hover:brightness-110">
136125
<img
137-
alt={claim.user.username}
126+
alt={User.handle(claim.user)}
138127
loading="lazy"
139128
decoding="async"
140129
class="rounded-full"
@@ -146,9 +135,7 @@ defmodule AlgoraWeb.Org.BountiesLive do
146135
</div>
147136
<div class="flex items-center gap-0.5">
148137
<div class="whitespace-nowrap text-sm font-medium text-gray-300 group-hover:text-gray-100">
149-
{length(@claims)} {if length(@claims) == 1,
150-
do: "claim",
151-
else: "claims"}
138+
{length(claims)} {ngettext("claim", "claims", length(claims))}
152139
</div>
153140
<svg
154141
xmlns="http://www.w3.org/2000/svg"
@@ -168,26 +155,19 @@ defmodule AlgoraWeb.Org.BountiesLive do
168155
</div>
169156
<% end %>
170157
</td>
171-
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle">
172-
<div class="min-w-[180px]">
173-
<div class="flex items-center justify-end gap-2">
174-
<!-- Add action buttons here (edit, delete, menu) -->
175-
</div>
176-
</div>
177-
</td>
178158
</tr>
179-
<%= for claim <- @claims do %>
159+
<%= for claim <- claims do %>
180160
<tr
181161
class="border-b border-white/15 bg-gray-950/50 transition-colors data-[state=selected]:bg-gray-100 hover:bg-gray-100/50 dark:data-[state=selected]:bg-gray-800 dark:hover:bg-gray-950/50"
182162
data-state="false"
183163
>
184-
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle">
164+
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle w-full">
185165
<div class="min-w-[250px]">
186166
<div class="flex items-center gap-3">
187167
<div class="flex -space-x-3">
188168
<div class="relative h-10 w-10 flex-shrink-0 rounded-full ring-4 ring-gray-800">
189169
<img
190-
alt={claim.user.username}
170+
alt={User.handle(claim.user)}
191171
loading="lazy"
192172
decoding="async"
193173
class="rounded-full"
@@ -198,7 +178,7 @@ defmodule AlgoraWeb.Org.BountiesLive do
198178
</div>
199179
<div>
200180
<div class="text-sm font-medium text-gray-200">
201-
{claim.user.username}
181+
{User.handle(claim.user)}
202182
</div>
203183
<div class="text-xs text-gray-400">
204184
{Algora.Util.time_ago(claim.inserted_at)}
@@ -207,23 +187,15 @@ defmodule AlgoraWeb.Org.BountiesLive do
207187
</div>
208188
</div>
209189
</td>
210-
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle"></td>
211190
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle">
212191
<div class="min-w-[180px]">
213192
<div class="flex items-center justify-end gap-4">
214-
<.link
215-
rel="noopener"
216-
class="inline-flex h-10 items-center justify-center rounded-md bg-gray-100 px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-100/80 dark:bg-white/15 dark:text-gray-50 dark:hover:bg-white/20"
217-
href={claim.pull_request_url}
218-
>
219-
View
220-
</.link>
221-
<.link
222-
class="inline-flex h-10 items-center justify-center rounded-md bg-emerald-600 px-4 py-2 text-sm font-medium text-white hover:bg-emerald-600/90"
223-
href="#"
224-
>
225-
Reward
226-
</.link>
193+
<.button variant="secondary">
194+
<.link href={claim.source.url}>View</.link>
195+
</.button>
196+
<.button>
197+
<.link href={~p"/claims/#{claim.group_id}"}>Reward</.link>
198+
</.button>
227199
</div>
228200
</div>
229201
</td>
@@ -247,7 +219,42 @@ defmodule AlgoraWeb.Org.BountiesLive do
247219
end
248220

249221
def handle_params(params, _uri, socket) do
250-
{:noreply, assign(socket, :current_tab, get_current_tab(params))}
222+
limit = 10
223+
current_org = socket.assigns.current_org
224+
current_tab = get_current_tab(params)
225+
226+
# TODO: fetch only bounties for the current tab
227+
open_bounties = Bounties.list_bounties(owner_id: current_org.id, limit: limit, status: :open)
228+
paid_bounties = Bounties.list_bounties(owner_id: current_org.id, limit: limit, status: :paid)
229+
230+
# TODO: fetch stats in one query
231+
open_count = length(open_bounties)
232+
paid_count = length(paid_bounties)
233+
234+
bounties =
235+
case current_tab do
236+
:open -> open_bounties
237+
:completed -> paid_bounties
238+
end
239+
240+
claims_by_ticket =
241+
bounties
242+
|> Enum.map(& &1.ticket.id)
243+
|> Bounties.list_claims()
244+
|> Enum.group_by(& &1.target_id)
245+
246+
bounties =
247+
Enum.map(bounties, fn bounty ->
248+
# TODO: group claims by group_id
249+
%{bounty: bounty, claims: Map.get(claims_by_ticket, bounty.ticket.id, [])}
250+
end)
251+
252+
{:noreply,
253+
socket
254+
|> assign(:current_tab, current_tab)
255+
|> assign(:bounties, bounties)
256+
|> assign(:open_count, open_count)
257+
|> assign(:completed_count, paid_count)}
251258
end
252259

253260
defp get_current_tab(params) do

0 commit comments

Comments
 (0)