Skip to content

Commit 81ad9df

Browse files
committed
feat: edit & delete bounties
1 parent 75909d1 commit 81ad9df

File tree

3 files changed

+218
-2
lines changed

3 files changed

+218
-2
lines changed

lib/algora/bounties/bounties.ex

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ defmodule Algora.Bounties do
1212
alias Algora.Bounties.Jobs
1313
alias Algora.Bounties.LineItem
1414
alias Algora.Bounties.Tip
15+
alias Algora.Github
1516
alias Algora.Organizations.Member
1617
alias Algora.Payments
1718
alias Algora.Payments.Transaction
1819
alias Algora.PSP
1920
alias Algora.Repo
2021
alias Algora.Util
2122
alias Algora.Workspace
23+
alias Algora.Workspace.CommandResponse
2224
alias Algora.Workspace.Installation
2325
alias Algora.Workspace.Ticket
2426

@@ -1585,4 +1587,17 @@ defmodule Algora.Bounties do
15851587
def get_attempt_emoji(%Attempt{status: :inactive}), do: "🔴"
15861588
def get_attempt_emoji(%Attempt{warnings_count: count}) when count > 0, do: "🟡"
15871589
def get_attempt_emoji(%Attempt{status: :active}), do: "🟢"
1590+
1591+
@spec delete_bounty(Bounty.t()) :: {:ok, Bounty.t()} | {:error, Ecto.Changeset.t()}
1592+
def delete_bounty(%Bounty{} = bounty) do
1593+
Repo.transact(fn ->
1594+
with {:ok, updated_bounty} <-
1595+
bounty
1596+
|> Bounty.changeset(%{status: :cancelled})
1597+
|> Repo.update() do
1598+
broadcast()
1599+
{:ok, updated_bounty}
1600+
end
1601+
end)
1602+
end
15881603
end

lib/algora/bounties/schemas/bounty.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ defmodule Algora.Bounties.Bounty do
4747
:shared_with,
4848
:hours_per_week,
4949
:hourly_rate,
50-
:contract_type
50+
:contract_type,
51+
:status
5152
])
5253
|> validate_required([:amount, :ticket_id, :owner_id, :creator_id])
5354
|> generate_id()

lib/algora_web/live/org/bounties_live.ex

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
defmodule AlgoraWeb.Org.BountiesLive do
22
@moduledoc false
33
use AlgoraWeb, :live_view
4+
use Ecto.Schema
5+
6+
import Ecto.Changeset
7+
import Ecto.Query
48

59
alias Algora.Accounts.User
610
alias Algora.Bounties
711
alias Algora.Bounties.Bounty
812
alias Algora.Payments
13+
alias Algora.Repo
14+
alias Algora.Types.USD
15+
alias Algora.Workspace
16+
17+
embedded_schema do
18+
field :amount, USD
19+
end
20+
21+
def edit_amount_changeset(attrs \\ %{}) do
22+
%__MODULE__{}
23+
|> cast(attrs, [:amount])
24+
|> validate_required([:amount])
25+
|> Algora.Validations.validate_money_positive(:amount)
26+
end
927

1028
def mount(_params, _session, socket) do
11-
{:ok, socket}
29+
{:ok,
30+
socket
31+
|> assign(:show_edit_modal, false)
32+
|> assign(:editing_bounty, nil)
33+
|> assign(:edit_form, to_form(edit_amount_changeset()))}
1234
end
1335

1436
def render(assigns) do
@@ -159,6 +181,26 @@ defmodule AlgoraWeb.Org.BountiesLive do
159181
</div>
160182
<% end %>
161183
</td>
184+
<td class="[&:has([role=checkbox])]:pr-0 p-4 align-middle">
185+
<div class="flex items-center gap-2">
186+
<.button
187+
phx-click="edit-bounty-amount"
188+
phx-value-id={bounty.id}
189+
variant="secondary"
190+
size="sm"
191+
>
192+
Edit Amount
193+
</.button>
194+
<.button
195+
phx-click="delete-bounty"
196+
phx-value-id={bounty.id}
197+
variant="destructive"
198+
size="sm"
199+
>
200+
Delete
201+
</.button>
202+
</div>
203+
</td>
162204
</tr>
163205
<%= for {_group_id, claims} <- claim_groups do %>
164206
<tr
@@ -268,6 +310,40 @@ defmodule AlgoraWeb.Org.BountiesLive do
268310
</div>
269311
</div>
270312
</div>
313+
314+
<!-- Edit Amount Drawer -->
315+
<.drawer show={@show_edit_modal} direction="right" on_cancel="cancel-edit">
316+
<.drawer_header>
317+
<.drawer_title>Edit Bounty Amount</.drawer_title>
318+
<.drawer_description>
319+
Update the bounty amount for this issue
320+
</.drawer_description>
321+
</.drawer_header>
322+
<.drawer_content>
323+
<.form
324+
for={@edit_form}
325+
as="edit_amount"
326+
phx-submit="save-bounty-amount"
327+
phx-change="validate-amount"
328+
class="space-y-4"
329+
>
330+
<.input
331+
label="New Amount"
332+
icon="tabler-currency-dollar"
333+
field={@edit_form[:amount]}
334+
placeholder="e.g., 1000"
335+
/>
336+
<div class="flex gap-3 justify-end">
337+
<.button type="button" variant="secondary" phx-click="cancel-edit">
338+
Cancel
339+
</.button>
340+
<.button type="submit" variant="default">
341+
Save
342+
</.button>
343+
</div>
344+
</.form>
345+
</.drawer_content>
346+
</.drawer>
271347
"""
272348
end
273349

@@ -287,6 +363,116 @@ defmodule AlgoraWeb.Org.BountiesLive do
287363
end}
288364
end
289365

366+
def handle_event("delete-bounty", %{"id" => bounty_id}, socket) do
367+
bounty = Repo.get!(Bounty, bounty_id)
368+
369+
case Bounties.delete_bounty(bounty) do
370+
{:ok, _bounty} ->
371+
{:noreply,
372+
socket
373+
|> put_flash(:info, "Bounty deleted successfully")
374+
|> assign_bounties()}
375+
376+
{:error, _changeset} ->
377+
{:noreply, put_flash(socket, :error, "Failed to delete bounty")}
378+
end
379+
end
380+
381+
def handle_event("edit-bounty-amount", %{"id" => bounty_id}, socket) do
382+
[bounty] = Bounties.list_bounties(id: bounty_id)
383+
changeset = edit_amount_changeset(%{amount: bounty.amount})
384+
385+
{:noreply,
386+
socket
387+
|> assign(:editing_bounty, bounty)
388+
|> assign(:edit_form, to_form(changeset))
389+
|> assign(:show_edit_modal, true)}
390+
end
391+
392+
def handle_event("validate-amount", params, socket) do
393+
form_params =
394+
case params do
395+
%{"edit_amount" => form_params} -> form_params
396+
%{"bounties_live" => form_params} -> form_params
397+
_ -> %{}
398+
end
399+
400+
changeset =
401+
form_params
402+
|> edit_amount_changeset()
403+
|> Map.put(:action, :validate)
404+
405+
{:noreply, assign(socket, :edit_form, to_form(changeset))}
406+
end
407+
408+
def handle_event("save-bounty-amount", params, socket) do
409+
form_params =
410+
case params do
411+
%{"edit_amount" => form_params} -> form_params
412+
%{"bounties_live" => form_params} -> form_params
413+
_ -> %{}
414+
end
415+
416+
changeset = edit_amount_changeset(form_params)
417+
418+
case apply_action(changeset, :update) do
419+
{:ok, %{amount: amount}} ->
420+
bounty =
421+
Bounty
422+
|> Repo.get(socket.assigns.editing_bounty.id)
423+
|> Repo.preload([:owner, [ticket: [repository: :user]]])
424+
425+
with {:ok, installation} <-
426+
Workspace.fetch_installation_by(
427+
provider: "github",
428+
connected_user_id: bounty.ticket.repository.user.id
429+
),
430+
{:ok, bounty} <-
431+
bounty
432+
|> Bounty.changeset(%{amount: amount})
433+
|> Repo.update(),
434+
{:ok, cr} <-
435+
Workspace.fetch_command_response(bounty.ticket_id, :bounty),
436+
{:ok, _job} <-
437+
Bounties.notify_bounty(
438+
%{
439+
owner: bounty.owner,
440+
bounty: bounty,
441+
ticket_ref: %{
442+
owner: bounty.ticket.repository.user.provider_login,
443+
repo: bounty.ticket.repository.name,
444+
number: bounty.ticket.number
445+
}
446+
},
447+
installation_id: installation.provider_id,
448+
command_id: cr.provider_command_id,
449+
command_source: cr.command_source
450+
) do
451+
{:noreply,
452+
socket
453+
|> assign(:show_edit_modal, false)
454+
|> assign(:editing_bounty, nil)
455+
|> assign(:edit_form, to_form(edit_amount_changeset()))
456+
|> put_flash(:info, "Bounty amount updated successfully")
457+
|> assign_bounties()}
458+
else
459+
{:error, _changeset} ->
460+
{:noreply, put_flash(socket, :error, "Failed to update bounty amount")}
461+
end
462+
463+
{:error, changeset} ->
464+
{:noreply, assign(socket, :edit_form, to_form(changeset))}
465+
end
466+
end
467+
468+
def handle_event("cancel-edit", _params, socket) do
469+
{:noreply,
470+
socket
471+
|> assign(:show_edit_modal, false)
472+
|> assign(:editing_bounty, nil)
473+
|> assign(:edit_form, to_form(edit_amount_changeset()))}
474+
end
475+
290476
def handle_event(_event, _params, socket) do
291477
{:noreply, socket}
292478
end
@@ -387,4 +573,18 @@ defmodule AlgoraWeb.Org.BountiesLive do
387573
end
388574

389575
defp page_size, do: 10
576+
577+
defp assign_bounties(socket) do
578+
current_org = socket.assigns.current_org
579+
580+
bounties =
581+
Bounties.list_bounties(
582+
owner_id: current_org.id,
583+
limit: page_size(),
584+
status: :open,
585+
current_user: socket.assigns[:current_user]
586+
)
587+
588+
assign(socket, :bounty_rows, to_bounty_rows(bounties))
589+
end
390590
end

0 commit comments

Comments
 (0)