Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ config :algora,
{"/create/org", "/onboarding/org"},
{"/solve", "/onboarding/dev"},
{"/onboarding/solver", "/onboarding/dev"},
{"/:org/contract/:id", "/:org/contracts/:id"},
{"/org/*path", "/*path"},
{"/@/:handle", "/:handle/profile"}
]
Expand Down
42 changes: 26 additions & 16 deletions lib/algora/bounties/bounties.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ defmodule Algora.Bounties do
amount: Money.t(),
ticket: Ticket.t(),
visibility: Bounty.visibility(),
shared_with: [String.t()]
shared_with: [String.t()],
hours_per_week: integer() | nil
}) ::
{:ok, Bounty.t()} | {:error, atom()}
defp do_create_bounty(%{creator: creator, owner: owner, amount: amount, ticket: ticket} = params) do
Expand All @@ -62,7 +63,8 @@ defmodule Algora.Bounties do
owner_id: owner.id,
creator_id: creator.id,
visibility: params[:visibility] || owner.bounty_mode,
shared_with: params[:shared_with] || []
shared_with: params[:shared_with] || [],
hours_per_week: params[:hours_per_week]
})

changeset
Expand Down Expand Up @@ -109,7 +111,8 @@ defmodule Algora.Bounties do
command_id: integer(),
command_source: :ticket | :comment,
visibility: Bounty.visibility() | nil,
shared_with: [String.t()] | nil
shared_with: [String.t()] | nil,
hours_per_week: integer() | nil
]
) ::
{:ok, Bounty.t()} | {:error, atom()}
Expand Down Expand Up @@ -140,7 +143,8 @@ defmodule Algora.Bounties do
amount: amount,
ticket: ticket,
visibility: opts[:visibility],
shared_with: shared_with
shared_with: shared_with,
hours_per_week: opts[:hours_per_week]
})

:set ->
Expand Down Expand Up @@ -190,7 +194,8 @@ defmodule Algora.Bounties do
opts :: [
strategy: strategy(),
visibility: Bounty.visibility() | nil,
shared_with: [String.t()] | nil
shared_with: [String.t()] | nil,
hours_per_week: integer() | nil
]
) ::
{:ok, Bounty.t()} | {:error, atom()}
Expand All @@ -209,7 +214,8 @@ defmodule Algora.Bounties do
amount: amount,
ticket: ticket,
visibility: opts[:visibility],
shared_with: shared_with
shared_with: shared_with,
hours_per_week: opts[:hours_per_week]
}),
{:ok, _job} <- notify_bounty(%{owner: owner, bounty: bounty}) do
broadcast()
Expand Down Expand Up @@ -862,6 +868,7 @@ defmodule Algora.Bounties do
initialize_charge(%{
id: Nanoid.generate(),
user_id: owner.id,
bounty_id: opts[:bounty_id],
gross_amount: gross_amount,
net_amount: amount,
total_fee: Money.sub!(gross_amount, amount),
Expand Down Expand Up @@ -961,23 +968,26 @@ defmodule Algora.Bounties do
end)
end

defp initialize_charge(%{
id: id,
user_id: user_id,
gross_amount: gross_amount,
net_amount: net_amount,
total_fee: total_fee,
line_items: line_items,
group_id: group_id,
idempotency_key: idempotency_key
}) do
defp initialize_charge(
%{
id: id,
user_id: user_id,
gross_amount: gross_amount,
net_amount: net_amount,
total_fee: total_fee,
line_items: line_items,
group_id: group_id,
idempotency_key: idempotency_key
} = params
) do
%Transaction{}
|> change(%{
id: id,
provider: "stripe",
type: :charge,
status: :initialized,
user_id: user_id,
bounty_id: params[:bounty_id],
gross_amount: gross_amount,
net_amount: net_amount,
total_fee: total_fee,
Expand Down
5 changes: 4 additions & 1 deletion lib/algora/bounties/schemas/bounty.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Algora.Bounties.Bounty do
field :visibility, Ecto.Enum, values: [:community, :exclusive, :public], null: false, default: :community
field :shared_with, {:array, :string}, null: false, default: []
field :deadline, :utc_datetime_usec
field :hours_per_week, :integer

belongs_to :ticket, Algora.Workspace.Ticket
belongs_to :owner, User
Expand All @@ -33,7 +34,7 @@ defmodule Algora.Bounties.Bounty do

def changeset(bounty, attrs) do
bounty
|> cast(attrs, [:amount, :ticket_id, :owner_id, :creator_id, :visibility, :shared_with])
|> cast(attrs, [:amount, :ticket_id, :owner_id, :creator_id, :visibility, :shared_with, :hours_per_week])
|> validate_required([:amount, :ticket_id, :owner_id, :creator_id])
|> generate_id()
|> foreign_key_constraint(:ticket)
Expand Down Expand Up @@ -67,6 +68,8 @@ defmodule Algora.Bounties.Bounty do
Algora.Util.path_from_url(url)
end

def path(_bounty), do: nil

def full_path(%{repository: %{name: name, owner: %{login: login}}, ticket: %{number: number}}) do
"#{login}/#{name}##{number}"
end
Expand Down
2 changes: 2 additions & 0 deletions lib/algora/organizations/schemas/member.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ defmodule Algora.Organizations.Member do
end

def can_create_bounty?(role), do: role in [:admin, :mod]

def can_create_contract?(role), do: role in [:admin, :mod]
end
27 changes: 27 additions & 0 deletions lib/algora/shared/validations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,33 @@ defmodule Algora.Validations do
end
end

def validate_github_handle(changeset, field, embed_field \\ nil) do
case get_change(changeset, field) do
handle when not is_nil(handle) ->
# Check if user is already embedded with matching provider_login
existing_user = embed_field && get_field(changeset, embed_field)

if existing_user && existing_user.provider_login == handle do
changeset
else
case Algora.Workspace.ensure_user(Algora.Admin.token!(), handle) do
{:ok, user} ->
if embed_field do
put_embed(changeset, embed_field, user)
else
changeset
end

{:error, error, _, _, _, _} ->
add_error(changeset, field, error)
end
end

_ ->
changeset
end
end

def validate_date_in_future(changeset, field) do
validate_change(changeset, field, fn _, date ->
if date && Date.before?(date, DateTime.utc_now()) do
Expand Down
83 changes: 54 additions & 29 deletions lib/algora_web/components/layouts/user.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -135,35 +135,6 @@
</ul>
<% end %>
</nav>
<%= if main_bounty_form = Map.get(assigns, :main_bounty_form) do %>
<div class="mt-auto mx-auto">
<.button
phx-click="open_main_bounty_form"
class="h-9 w-9 rounded-md flex items-center justify-center relative"
>
<.icon name="tabler-diamond" class="h-6 w-6 shrink-0" />
<.icon
name="tabler-plus"
class="h-[0.8rem] w-[0.8rem] shrink-0 absolute bottom-[0.2rem] right-[0.2rem]"
/>
</.button>
<.drawer
show={@main_bounty_form_open?}
direction="right"
on_cancel="close_main_bounty_form"
>
<.drawer_header>
<.drawer_title>Create new bounty</.drawer_title>
<.drawer_description>
Create and fund a bounty for an issue
</.drawer_description>
</.drawer_header>
<.drawer_content class="mt-4">
<AlgoraWeb.Forms.BountyForm.bounty_form form={main_bounty_form} />
</.drawer_content>
</.drawer>
</div>
<% end %>
</div>

<div class="lg:pl-16">
Expand Down Expand Up @@ -237,6 +208,60 @@
</div>
</.link>
<%= if @current_user do %>
<%= if main_contract_form = Map.get(assigns, :main_contract_form) do %>
<div>
<.button
phx-click="open_main_contract_form"
class="h-9 w-9 rounded-md flex items-center justify-center relative"
>
<.icon name="tabler-user-dollar" class="h-6 w-6 shrink-0" />
</.button>
<.drawer
show={@main_contract_form_open?}
direction="right"
on_cancel="close_main_contract_form"
>
<.drawer_header>
<.drawer_title>Create new contract</.drawer_title>
<.drawer_description>
Engage a developer for ongoing work
</.drawer_description>
</.drawer_header>
<.drawer_content class="mt-4">
<AlgoraWeb.Forms.ContractForm.contract_form form={main_contract_form} />
</.drawer_content>
</.drawer>
</div>
<% end %>
<%= if main_bounty_form = Map.get(assigns, :main_bounty_form) do %>
<div>
<.button
phx-click="open_main_bounty_form"
class="h-9 w-9 rounded-md flex items-center justify-center relative"
>
<.icon name="tabler-diamond" class="h-6 w-6 shrink-0" />
<.icon
name="tabler-plus"
class="h-[0.8rem] w-[0.8rem] shrink-0 absolute bottom-[0.2rem] right-[0.2rem]"
/>
</.button>
<.drawer
show={@main_bounty_form_open?}
direction="right"
on_cancel="close_main_bounty_form"
>
<.drawer_header>
<.drawer_title>Create new bounty</.drawer_title>
<.drawer_description>
Create and fund a bounty for an issue
</.drawer_description>
</.drawer_header>
<.drawer_content class="mt-4">
<AlgoraWeb.Forms.BountyForm.bounty_form form={main_bounty_form} />
</.drawer_content>
</.drawer>
</div>
<% end %>
<%!-- {live_render(@socket, AlgoraWeb.Activity.UserNavTimelineLive,
id: "activity-timeline",
session: %{},
Expand Down
Loading