@@ -4,15 +4,22 @@ defmodule AlgoraWeb.Org.DashboardLive do
44
55 import AlgoraWeb.Components.Achievement
66 import Ecto.Changeset
7+ import Ecto.Query
78
89 alias Algora.Accounts
910 alias Algora.Accounts.User
1011 alias Algora.Bounties
12+ alias Algora.Bounties.Bounty
13+ alias Algora.Bounties.Claim
1114 alias Algora.Contracts
1215 alias Algora.Github
16+ alias Algora.Payments.Transaction
17+ alias Algora.Repo
1318 alias Algora.Types.USD
1419 alias Algora.Validations
1520 alias Algora.Workspace
21+ alias Algora.Workspace.Repository
22+ alias Algora.Workspace.Ticket
1623 alias AlgoraWeb.Components.Logos
1724
1825 require Logger
@@ -107,6 +114,7 @@ defmodule AlgoraWeb.Org.DashboardLive do
107114 |> assign ( :contract_form , to_form ( ContractForm . changeset ( % ContractForm { } , % { } ) ) )
108115 |> assign ( :show_contract_modal , false )
109116 |> assign ( :selected_developer , nil )
117+ |> assign_payable_bounties ( )
110118 |> assign_contracts ( )
111119 |> assign_achievements ( ) }
112120 else
@@ -119,6 +127,115 @@ defmodule AlgoraWeb.Org.DashboardLive do
119127 ~H"""
120128 < div class = "lg:pr-96 " >
121129 < div class = "container mx-auto max-w-7xl space-y-8 p-8 " >
130+ < . section :if = { @ payable_bounties != [ ] } >
131+ < . card >
132+ < . card_header >
133+ < . card_title > Pending Payments</ . card_title >
134+ < . card_description >
135+ The following claims have been approved and are ready for payment.
136+ </ . card_description >
137+ </ . card_header >
138+ < . card_content >
139+ < table class = "w-full caption-bottom text-sm " >
140+ < tbody class = "[&_tr:last-child]:border-0 " >
141+ <%= for { _group_id , [ % { target: % { bounties: [ bounty | _ ] } } | _ ] = claims } <- @ payable_bounties do %>
142+ < tr
143+ 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%] "
144+ data-state = "false "
145+ >
146+ < td colspan = { 2 } class = "[&:has([role=checkbox])]:pr-0 p-4 align-middle " >
147+ < div class = "min-w-[250px] " >
148+ < div class = "group relative flex h-full flex-col " >
149+ < div class = "relative h-full pl-2 " >
150+ < div class = "flex items-start justify-between " >
151+ < div class = "cursor-pointer font-mono text-2xl " >
152+ < div class = "font-extrabold text-emerald-300 hover:text-emerald-200 " >
153+ { Money . to_string! ( bounty . amount ) }
154+ </ div >
155+ </ div >
156+ </ div >
157+ < . link
158+ rel = "noopener "
159+ class = "group/issue inline-flex flex-col "
160+ href = { Bounty . url ( bounty ) }
161+ >
162+ < div class = "flex items-center gap-4 " >
163+ < div class = "truncate " >
164+ < p class = "truncate text-sm font-medium text-gray-300 group-hover/issue:text-gray-200 group-hover/issue:underline " >
165+ { Bounty . path ( bounty ) }
166+ </ p >
167+ </ div >
168+ </ div >
169+ < p class = "line-clamp-2 break-words text-base font-medium leading-tight text-gray-100 group-hover/issue:text-white group-hover/issue:underline " >
170+ { bounty . ticket . title }
171+ </ p >
172+ </ . link >
173+ < p class = "flex items-center gap-1.5 text-xs text-gray-400 " >
174+ { Algora.Util . time_ago ( bounty . inserted_at ) }
175+ </ p >
176+ </ div >
177+ </ div >
178+ </ div >
179+ </ td >
180+ </ tr >
181+ < tr
182+ 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 "
183+ data-state = "false "
184+ >
185+ < td class = "[&:has([role=checkbox])]:pr-0 p-4 align-middle w-full " >
186+ < div class = "min-w-[250px] " >
187+ < div class = "flex items-center gap-3 " >
188+ < div class = "flex -space-x-3 " >
189+ <%= for claim <- claims do %>
190+ < div class = "relative h-10 w-10 flex-shrink-0 rounded-full ring-4 ring-background " >
191+ < img
192+ alt = { User . handle ( claim . user ) }
193+ loading = "lazy "
194+ decoding = "async "
195+ class = "rounded-full "
196+ src = { claim . user . avatar_url }
197+ style = "position: absolute; height: 100%; width: 100%; inset: 0px; color: transparent; "
198+ />
199+ </ div >
200+ <% end %>
201+ </ div >
202+ < div >
203+ < div class = "text-sm font-medium text-gray-200 " >
204+ { claims
205+ |> Enum . map ( fn c -> User . handle ( c . user ) end )
206+ |> Algora.Util . format_name_list ( ) }
207+ </ div >
208+ < div class = "text-xs text-gray-400 " >
209+ { Algora.Util . time_ago ( hd ( claims ) . inserted_at ) }
210+ </ div >
211+ </ div >
212+ </ div >
213+ </ div >
214+ </ td >
215+ < td class = "[&:has([role=checkbox])]:pr-0 p-4 align-middle " >
216+ < div class = "min-w-[180px] " >
217+ < div class = "flex items-center justify-end gap-4 " >
218+ < . button
219+ :if = { hd ( claims ) . source }
220+ href = { hd ( claims ) . source . url }
221+ variant = "secondary "
222+ >
223+ View
224+ </ . button >
225+ < . button href = { ~p" /claims/#{ hd ( claims ) . group_id } " } >
226+ Reward
227+ </ . button >
228+ </ div >
229+ </ div >
230+ </ td >
231+ </ tr >
232+ <% end %>
233+ </ tbody >
234+ </ table >
235+ </ . card_content >
236+ </ . card >
237+ </ . section >
238+
122239 < . section :if = { @ installations == [ ] } >
123240 < . card >
124241 < . card_header >
@@ -635,6 +752,34 @@ defmodule AlgoraWeb.Org.DashboardLive do
635752 end
636753 end
637754
755+ defp assign_payable_bounties ( socket ) do
756+ org = socket . assigns . current_org
757+
758+ payable_claims =
759+ Repo . all (
760+ from c in Claim ,
761+ where: c . status == :approved ,
762+ join: t in assoc ( c , :target ) ,
763+ join: b in assoc ( t , :bounties ) ,
764+ where: b . owner_id == ^ org . id ,
765+ left_join: tx in Transaction ,
766+ on: tx . claim_id == c . id and tx . type == :debit ,
767+ where: is_nil ( tx . status ) or tx . status not in [ :initialized , :succeeded ] ,
768+ join: r in assoc ( t , :repository ) ,
769+ join: ru in assoc ( r , :user ) ,
770+ join: cu in assoc ( c , :user ) ,
771+ left_join: s in assoc ( c , :source ) ,
772+ select_merge: % {
773+ user: cu ,
774+ source: s ,
775+ target: % Ticket { t | bounties: [ % Bounty { b | ticket: % { t | repository: % { r | user: ru } } } ] }
776+ }
777+ )
778+
779+ payable_bounties = Enum . group_by ( payable_claims , & & 1 . group_id )
780+ assign ( socket , :payable_bounties , payable_bounties )
781+ end
782+
638783 defp assign_contracts ( socket ) do
639784 contracts = Contracts . list_contracts ( client_id: socket . assigns . current_org . id , status: :draft )
640785
0 commit comments