Skip to content

Commit 275fb7c

Browse files
authored
feat: engage top-performing devs with contract opps (#81)
1 parent 937d22e commit 275fb7c

File tree

9 files changed

+399
-77
lines changed

9 files changed

+399
-77
lines changed

lib/algora/accounts/accounts.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ defmodule Algora.Accounts do
134134
id: u.id,
135135
handle: u.handle,
136136
name: u.name,
137+
provider_login: u.provider_login,
138+
provider_meta: u.provider_meta,
137139
avatar_url: u.avatar_url,
138140
bio: u.bio,
139141
country: u.country,

lib/algora/contracts/contracts.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@ defmodule Algora.Contracts do
2626
@type criterion ::
2727
{:id, binary()}
2828
| {:client_id, binary()}
29+
| {:contractor_id, binary()}
2930
| {:original_contract_id, binary()}
3031
| {:open?, true}
3132
| {:active_or_paid?, true}
3233
| {:original?, true}
33-
| {:status, :open | :paid}
34+
| {:status, :draft | :active | :paid}
3435
| {:after, non_neg_integer()}
3536
| {:before, non_neg_integer()}
3637
| {:order, :asc | :desc}
@@ -665,6 +666,9 @@ defmodule Algora.Contracts do
665666
{:id, id}, query ->
666667
from([c] in query, where: c.id == ^id)
667668

669+
{:contractor_id, contractor_id}, query ->
670+
from([c] in query, where: c.contractor_id == ^contractor_id)
671+
668672
{:client_id, client_id}, query ->
669673
from([c] in query, where: c.client_id == ^client_id)
670674

lib/algora/contracts/schemas/contract.ex

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ defmodule Algora.Contracts.Contract do
66
alias Algora.Activities.Activity
77
alias Algora.Contracts.Contract
88
alias Algora.MoneyUtils
9+
alias Algora.Validations
910

1011
typed_schema "contracts" do
1112
field :status, Ecto.Enum, values: [:draft, :active, :paid, :cancelled, :disputed]
@@ -78,51 +79,28 @@ defmodule Algora.Contracts.Contract do
7879
:status,
7980
:sequence_number,
8081
:hourly_rate,
82+
:hourly_rate_min,
83+
:hourly_rate_max,
8184
:hours_per_week,
8285
:start_date,
8386
:end_date,
8487
:original_contract_id,
8588
:client_id,
8689
:contractor_id
8790
])
88-
|> validate_required([
89-
:status,
90-
:hourly_rate,
91-
:hours_per_week,
92-
:start_date,
93-
:client_id,
94-
:contractor_id
95-
])
91+
|> validate_required([:status, :hours_per_week, :client_id])
9692
|> validate_number(:hours_per_week, greater_than: 0)
97-
|> validate_number(:hourly_rate, greater_than: 0)
93+
|> Validations.validate_money_positive(:hourly_rate)
9894
|> foreign_key_constraint(:client_id)
9995
|> foreign_key_constraint(:contractor_id)
10096
|> generate_id()
97+
|> put_original_contract_id()
10198
end
10299

103-
def draft_changeset(contract, attrs) do
104-
contract
105-
|> cast(attrs, [
106-
:status,
107-
:sequence_number,
108-
:hourly_rate_min,
109-
:hourly_rate_max,
110-
:hours_per_week,
111-
:start_date,
112-
:end_date,
113-
:original_contract_id,
114-
:client_id
115-
])
116-
|> validate_required([
117-
:status,
118-
:hourly_rate_min,
119-
:hourly_rate_max,
120-
:hours_per_week,
121-
:start_date,
122-
:client_id
123-
])
124-
|> validate_number(:hours_per_week, greater_than: 0)
125-
|> foreign_key_constraint(:client_id)
126-
|> generate_id()
100+
def put_original_contract_id(changeset) do
101+
case get_field(changeset, :original_contract_id) do
102+
nil -> put_change(changeset, :original_contract_id, get_field(changeset, :id))
103+
_existing -> changeset
104+
end
127105
end
128106
end

lib/algora/organizations/organizations.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ defmodule Algora.Organizations do
4343
)
4444

4545
contract_changeset =
46-
Contract.draft_changeset(
46+
Contract.changeset(
4747
%Contract{},
4848
Map.put(params.contract, :client_id, org.id)
4949
)

lib/algora_web/live/contract/modals/dispute_drawer.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ defmodule AlgoraWeb.Contract.Modals.DisputeDrawer do
6464
{Money.to_string!(@contract.amount_debited)}
6565
</dd>
6666
</div>
67-
<div class="flex justify-between">
67+
<div :if={@contract.start_date} class="flex justify-between">
6868
<dt class="text-muted-foreground">Contract Period</dt>
6969
<dd class="font-semibold">
7070
{Calendar.strftime(@contract.start_date, "%b %d")} - {Calendar.strftime(

lib/algora_web/live/contract/view_live.ex

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,25 @@ defmodule AlgoraWeb.Contract.ViewLive do
3838
<h1 class="text-2xl font-semibold">
3939
Contract with {@contract.contractor.name}
4040
</h1>
41-
<p class="text-sm text-muted-foreground">
42-
Started {Calendar.strftime(@contract.start_date, "%b %d, %Y")}
43-
</p>
41+
<%= if @contract.start_date do %>
42+
<p class="text-sm text-muted-foreground">
43+
Started {Calendar.strftime(@contract.start_date, "%b %d, %Y")}
44+
</p>
45+
<% else %>
46+
<p class="text-sm text-muted-foreground">
47+
Drafted on {Calendar.strftime(@contract.inserted_at, "%b %d, %Y")}
48+
</p>
49+
<% end %>
4450
</div>
4551
</div>
46-
<div>
47-
<.badge variant="success">Active</.badge>
48-
</div>
52+
<%= case @contract.status do %>
53+
<% :draft -> %>
54+
<.badge variant="warning">Draft</.badge>
55+
<% :active -> %>
56+
<.badge variant="success">Active</.badge>
57+
<% _ -> %>
58+
<.badge variant="destructive">Inactive</.badge>
59+
<% end %>
4960
</div>
5061
<!-- Stats Grid -->
5162
<div class="mt-8 grid grid-cols-4 gap-4">
@@ -84,14 +95,14 @@ defmodule AlgoraWeb.Contract.ViewLive do
8495
</.card>
8596
</div>
8697
<!-- Tabs -->
87-
<.tabs :let={builder} id="contract-tabs" default="payments" class="mt-8">
98+
<.tabs :let={builder} id="contract-tabs" default="details" class="mt-8">
8899
<.tabs_list class="flex w-full space-x-1 rounded-lg bg-muted p-1">
89-
<.tabs_trigger builder={builder} value="payments" class="flex-1">
90-
<.icon name="tabler-credit-card" class="mr-2 h-4 w-4" /> Payments
91-
</.tabs_trigger>
92100
<.tabs_trigger builder={builder} value="details" class="flex-1">
93101
<.icon name="tabler-file-text" class="mr-2 h-4 w-4" /> Contract Details
94102
</.tabs_trigger>
103+
<.tabs_trigger builder={builder} value="payments" class="flex-1">
104+
<.icon name="tabler-credit-card" class="mr-2 h-4 w-4" /> Payments
105+
</.tabs_trigger>
95106
<.tabs_trigger builder={builder} value="activity" class="flex-1">
96107
<.icon name="tabler-history" class="mr-2 h-4 w-4" /> Activity
97108
</.tabs_trigger>
@@ -106,7 +117,7 @@ defmodule AlgoraWeb.Contract.ViewLive do
106117
</.card_description>
107118
</.card_header>
108119
<.card_content>
109-
<div class="space-y-8">
120+
<div :if={@contract.timesheet} class="space-y-8">
110121
<%= for contract <- @contract_chain do %>
111122
<%= case Contracts.get_payment_status(contract) do %>
112123
<% {:pending_timesheet, contract} -> %>
@@ -119,7 +130,7 @@ defmodule AlgoraWeb.Contract.ViewLive do
119130
<div class="font-medium">
120131
Waiting for timesheet submission
121132
</div>
122-
<div class="text-sm text-muted-foreground">
133+
<div :if={contract.start_date} class="text-sm text-muted-foreground">
123134
{Calendar.strftime(contract.start_date, "%b %d")} - {Calendar.strftime(
124135
contract.end_date,
125136
"%b %d, %Y"
@@ -142,7 +153,7 @@ defmodule AlgoraWeb.Contract.ViewLive do
142153
<div class="font-medium">
143154
Ready to release payment for {contract.timesheet.hours_worked} hours
144155
</div>
145-
<div class="text-sm text-muted-foreground">
156+
<div :if={contract.start_date} class="text-sm text-muted-foreground">
146157
{Calendar.strftime(contract.start_date, "%b %d")} - {Calendar.strftime(
147158
contract.end_date,
148159
"%b %d, %Y"
@@ -464,19 +475,30 @@ defmodule AlgoraWeb.Contract.ViewLive do
464475
thread = Chat.get_or_create_thread!(contract)
465476
messages = thread.id |> Chat.list_messages() |> Repo.preload(:sender)
466477

467-
{:ok,
468-
socket
469-
|> assign(:contract, contract)
470-
|> assign(:contract_chain, contract_chain)
471-
|> assign(:has_more, length(contract_chain) >= page_size())
472-
|> assign(:page_title, "Contract with #{contract.contractor.name}")
473-
|> assign(:messages, messages)
474-
|> assign(:thread, thread)
475-
|> assign(:show_release_renew_modal, false)
476-
|> assign(:show_release_modal, false)
477-
|> assign(:show_dispute_modal, false)
478-
|> assign(:fee_data, Contracts.calculate_fee_data(contract))
479-
|> assign(:org_members, Organizations.list_org_members(contract.client))}
478+
case socket.assigns[:current_user] do
479+
nil ->
480+
{:ok, redirect(socket, to: ~p"/auth/login?return_to=#{~p"/org/#{contract.client.handle}/contracts/#{id}"}")}
481+
482+
current_user ->
483+
if current_user.id != contract.contractor_id and
484+
not (socket.assigns.all_contexts |> Enum.map(& &1.id) |> Enum.member?(contract.client_id)) do
485+
{:ok, raise(AlgoraWeb.NotFoundError)}
486+
else
487+
{:ok,
488+
socket
489+
|> assign(:contract, contract)
490+
|> assign(:contract_chain, contract_chain)
491+
|> assign(:has_more, length(contract_chain) >= page_size())
492+
|> assign(:page_title, "Contract with #{contract.contractor.name}")
493+
|> assign(:messages, messages)
494+
|> assign(:thread, thread)
495+
|> assign(:show_release_renew_modal, false)
496+
|> assign(:show_release_modal, false)
497+
|> assign(:show_dispute_modal, false)
498+
|> assign(:fee_data, Contracts.calculate_fee_data(contract))
499+
|> assign(:org_members, Organizations.list_org_members(contract.client))}
500+
end
501+
end
480502
end
481503

482504
def handle_event("send_message", %{"message" => content}, socket) do

0 commit comments

Comments
 (0)