Skip to content

Commit d0fbaab

Browse files
committed
add reward bounty drawer
1 parent e1c0c60 commit d0fbaab

File tree

5 files changed

+435
-166
lines changed

5 files changed

+435
-166
lines changed

lib/algora/bounties/bounties.ex

Lines changed: 103 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -284,86 +284,123 @@ defmodule Algora.Bounties do
284284
)
285285
end
286286

287-
@spec create_payment_session(
288-
%{creator: User.t(), amount: Money.t(), description: String.t()},
287+
# TODO: move to separate module
288+
defmodule LineItem do
289+
@moduledoc false
290+
defstruct [:amount, :title, :description, :image, :type]
291+
292+
@type t :: %__MODULE__{
293+
amount: Money.t(),
294+
title: String.t(),
295+
description: String.t() | nil,
296+
image: String.t() | nil,
297+
type: :payment | :fee
298+
}
299+
300+
def to_stripe(line_item) do
301+
%{
302+
price_data: %{
303+
unit_amount: MoneyUtils.to_minor_units(line_item.amount),
304+
currency: to_string(line_item.amount.currency),
305+
product_data: %{
306+
name: line_item.title,
307+
description: line_item.description,
308+
images: if(line_item.image, do: [line_item.image])
309+
}
310+
},
311+
quantity: 1
312+
}
313+
end
314+
315+
def gross_amount(line_items) do
316+
Enum.reduce(line_items, Money.zero(:USD), fn item, acc -> Money.add!(acc, item.amount) end)
317+
end
318+
319+
def total_fee(line_items) do
320+
Enum.reduce(line_items, Money.zero(:USD), fn item, acc ->
321+
if item.type == :fee, do: Money.add!(acc, item.amount), else: acc
322+
end)
323+
end
324+
end
325+
326+
@spec generate_line_items(
327+
%{amount: Money.t()},
289328
opts :: [
290329
ticket_ref: %{owner: String.t(), repo: String.t(), number: integer()},
291-
tip_id: String.t(),
292-
bounty_id: String.t(),
293330
claims: [Claim.t()],
294331
recipient: User.t()
295332
]
296333
) ::
297-
{:ok, String.t()} | {:error, atom()}
298-
def create_payment_session(%{creator: creator, amount: amount, description: description}, opts \\ []) do
334+
[LineItem.t()]
335+
def generate_line_items(%{amount: amount}, opts \\ []) do
299336
ticket_ref = opts[:ticket_ref]
300337
recipient = opts[:recipient]
301338
claims = opts[:claims]
302339

303-
tx_group_id = Nanoid.generate()
340+
description = if(ticket_ref, do: "#{ticket_ref[:owner]}/#{ticket_ref[:repo]}##{ticket_ref[:number]}")
304341

305-
# Calculate fees
306-
currency = to_string(amount.currency)
307342
platform_fee_pct = FeeTier.calculate_fee_percentage(Money.zero(:USD))
308343
transaction_fee_pct = Payments.get_transaction_fee_pct()
309344

310-
platform_fee = Money.mult!(amount, platform_fee_pct)
311-
transaction_fee = Money.mult!(amount, transaction_fee_pct)
312-
total_fee = Money.add!(platform_fee, transaction_fee)
313-
gross_amount = Money.add!(amount, total_fee)
345+
if recipient do
346+
[
347+
%LineItem{
348+
amount: amount,
349+
title: "Payment to @#{recipient.provider_login}",
350+
description: description,
351+
image: recipient.avatar_url,
352+
type: :payment
353+
}
354+
]
355+
else
356+
[]
357+
end ++
358+
Enum.map(claims, fn claim ->
359+
%LineItem{
360+
# TODO: ensure shares are normalized
361+
amount: Money.mult!(amount, claim.group_share),
362+
title: "Payment to @#{claim.user.provider_login}",
363+
description: description,
364+
image: claim.user.avatar_url,
365+
type: :payment
366+
}
367+
end) ++
368+
[
369+
%LineItem{
370+
amount: Money.mult!(amount, platform_fee_pct),
371+
title: "Algora platform fee (#{Util.format_pct(platform_fee_pct)})",
372+
type: :fee
373+
},
374+
%LineItem{
375+
amount: Money.mult!(amount, transaction_fee_pct),
376+
title: "Transaction fee (#{Util.format_pct(transaction_fee_pct)})",
377+
type: :fee
378+
}
379+
]
380+
end
381+
382+
@spec create_payment_session(
383+
%{creator: User.t(), amount: Money.t(), description: String.t()},
384+
opts :: [
385+
ticket_ref: %{owner: String.t(), repo: String.t(), number: integer()},
386+
tip_id: String.t(),
387+
bounty_id: String.t(),
388+
claims: [Claim.t()],
389+
recipient: User.t()
390+
]
391+
) ::
392+
{:ok, String.t()} | {:error, atom()}
393+
def create_payment_session(%{creator: creator, amount: amount, description: description}, opts \\ []) do
394+
tx_group_id = Nanoid.generate()
314395

315396
line_items =
316-
if recipient do
317-
[
318-
%{
319-
price_data: %{
320-
unit_amount: MoneyUtils.to_minor_units(amount),
321-
currency: currency,
322-
product_data: %{
323-
name: "Payment to @#{recipient.provider_login}",
324-
description: if(ticket_ref, do: "#{ticket_ref[:owner]}/#{ticket_ref[:repo]}##{ticket_ref[:number]}"),
325-
images: [recipient.avatar_url]
326-
}
327-
},
328-
quantity: 1
329-
}
330-
]
331-
else
332-
[]
333-
end ++
334-
Enum.map(claims, fn claim ->
335-
%{
336-
price_data: %{
337-
# TODO: ensure shares are normalized
338-
unit_amount: amount |> Money.mult!(claim.group_share) |> MoneyUtils.to_minor_units(),
339-
currency: currency,
340-
product_data: %{
341-
name: "Payment to @#{claim.user.provider_login}",
342-
description: if(ticket_ref, do: "#{ticket_ref[:owner]}/#{ticket_ref[:repo]}##{ticket_ref[:number]}"),
343-
images: [claim.user.avatar_url]
344-
}
345-
},
346-
quantity: 1
347-
}
348-
end) ++
349-
[
350-
%{
351-
price_data: %{
352-
unit_amount: MoneyUtils.to_minor_units(Money.mult!(amount, platform_fee_pct)),
353-
currency: currency,
354-
product_data: %{name: "Algora platform fee (#{Util.format_pct(platform_fee_pct)})"}
355-
},
356-
quantity: 1
357-
},
358-
%{
359-
price_data: %{
360-
unit_amount: MoneyUtils.to_minor_units(Money.mult!(amount, transaction_fee_pct)),
361-
currency: currency,
362-
product_data: %{name: "Transaction fee (#{Util.format_pct(transaction_fee_pct)})"}
363-
},
364-
quantity: 1
365-
}
366-
]
397+
generate_line_items(%{amount: amount},
398+
ticket_ref: opts[:ticket_ref],
399+
recipient: opts[:recipient],
400+
claims: opts[:claims]
401+
)
402+
403+
gross_amount = LineItem.gross_amount(line_items)
367404

368405
Repo.transact(fn ->
369406
with {:ok, _charge} <-
@@ -375,7 +412,7 @@ defmodule Algora.Bounties do
375412
user_id: creator.id,
376413
gross_amount: gross_amount,
377414
net_amount: amount,
378-
total_fee: total_fee,
415+
total_fee: Money.sub!(gross_amount, amount),
379416
line_items: line_items,
380417
group_id: tx_group_id
381418
}),
@@ -389,7 +426,7 @@ defmodule Algora.Bounties do
389426
group_id: tx_group_id
390427
}),
391428
{:ok, session} <-
392-
Payments.create_stripe_session(line_items, %{
429+
Payments.create_stripe_session(LineItem.to_stripe(line_items), %{
393430
description: description,
394431
metadata: %{"version" => "2", "group_id" => tx_group_id}
395432
}) do

lib/algora_web/components/core_components.ex

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule AlgoraWeb.CoreComponents do
1313
use Gettext, backend: AlgoraWeb.Gettext
1414

1515
alias AlgoraWeb.Components.UI.Accordion
16+
alias AlgoraWeb.Components.UI.Alert
1617
alias AlgoraWeb.Components.UI.Avatar
1718
alias AlgoraWeb.Components.UI.Card
1819
alias AlgoraWeb.Components.UI.Dialog
@@ -231,18 +232,19 @@ defmodule AlgoraWeb.CoreComponents do
231232
slot :link do
232233
attr :navigate, :string
233234
attr :href, :string
235+
attr :patch, :string
234236
attr :method, :any
235237
end
236238

237239
def dropdown2(assigns) do
238240
~H"""
239241
<!-- User account dropdown -->
240242
<div class={classes(["relative w-full text-left", @class])}>
241-
<div>
243+
<div class="overflow-visible relative">
242244
<button
243245
id={@id}
244246
type="button"
245-
class="group w-full rounded-md px-3.5 py-2 text-left text-sm font-medium text-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring"
247+
class="group w-full rounded-md p-3 text-left text-sm font-medium text-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring border border-input"
246248
phx-click={show_dropdown("##{@id}-dropdown")}
247249
phx-hook="Menu"
248250
data-active-class="bg-accent"
@@ -265,7 +267,7 @@ defmodule AlgoraWeb.CoreComponents do
265267
</span>
266268
<.icon
267269
name="tabler-selector"
268-
class="ml-2 h-5 w-5 flex-shrink-0 text-gray-500 group-hover:text-gray-400"
270+
class="ml-2 h-6 w-6 flex-shrink-0 text-gray-500 group-hover:text-gray-400"
269271
/>
270272
</span>
271273
</button>
@@ -282,7 +284,8 @@ defmodule AlgoraWeb.CoreComponents do
282284
<.link
283285
tabindex="-1"
284286
role="menuitem"
285-
class="block px-4 py-2 text-sm text-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring"
287+
class="block p-3 text-sm text-foreground hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring"
288+
phx-click={hide_dropdown("##{@id}-dropdown")}
286289
{link}
287290
>
288291
{render_slot(link)}
@@ -857,8 +860,8 @@ defmodule AlgoraWeb.CoreComponents do
857860
value={Phoenix.HTML.Form.normalize_value(@type, @value)}
858861
class={[
859862
"py-[7px] px-[11px] block w-full rounded-lg border-input bg-background",
860-
"text-foreground focus:outline-none focus:ring-4 sm:text-sm sm:leading-6",
861-
"border-input focus:border-ring focus:ring-ring/5",
863+
"text-foreground focus:outline-none focus:ring-1 sm:text-sm sm:leading-6",
864+
"border-input focus:border-ring focus:ring-ring",
862865
@errors != [] &&
863866
"border-destructive placeholder-destructive-foreground/50 focus:border-destructive focus:ring-destructive/10",
864867
@icon && "pl-10",
@@ -1258,7 +1261,9 @@ defmodule AlgoraWeb.CoreComponents do
12581261
defdelegate accordion_item(assigns), to: Accordion
12591262
defdelegate accordion_trigger(assigns), to: Accordion
12601263
defdelegate accordion(assigns), to: Accordion
1261-
defdelegate alert(assigns), to: AlgoraWeb.Components.UI.Alert
1264+
defdelegate alert_description(assigns), to: Alert
1265+
defdelegate alert_title(assigns), to: Alert
1266+
defdelegate alert(assigns), to: Alert
12621267
defdelegate avatar_fallback(assigns), to: Avatar
12631268
defdelegate avatar_image(assigns), to: Avatar
12641269
defdelegate avatar(assigns), to: Avatar
@@ -1305,7 +1310,6 @@ defmodule AlgoraWeb.CoreComponents do
13051310
defdelegate popover_content(assigns), to: Popover
13061311
defdelegate popover_trigger(assigns), to: Popover
13071312
defdelegate popover(assigns), to: Popover
1308-
defdelegate radio_group_item(assigns), to: RadioGroup
13091313
defdelegate radio_group(assigns), to: RadioGroup
13101314
defdelegate scroll_area(assigns), to: AlgoraWeb.Components.UI.ScrollArea
13111315
defdelegate select_content(assigns), to: Select

lib/algora_web/components/ui/drawer.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ defmodule AlgoraWeb.Components.UI.Drawer do
114114

115115
def drawer_content(assigns) do
116116
~H"""
117-
<div class={classes(["overflow-y-auto", @class])} {@rest}>
117+
<div class={classes(["overflow-y-auto px-1 -mx-1", @class])} {@rest}>
118118
{render_slot(@inner_block)}
119119
</div>
120120
"""

0 commit comments

Comments
 (0)