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
2 changes: 2 additions & 0 deletions lib/algora/accounts/accounts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ defmodule Algora.Accounts do
id: u.id,
handle: u.handle,
name: u.name,
provider_login: u.provider_login,
provider_meta: u.provider_meta,
avatar_url: u.avatar_url,
bio: u.bio,
country: u.country,
Expand Down
6 changes: 5 additions & 1 deletion lib/algora/contracts/contracts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ defmodule Algora.Contracts do
@type criterion ::
{:id, binary()}
| {:client_id, binary()}
| {:contractor_id, binary()}
| {:original_contract_id, binary()}
| {:open?, true}
| {:active_or_paid?, true}
| {:original?, true}
| {:status, :open | :paid}
| {:status, :draft | :active | :paid}
| {:after, non_neg_integer()}
| {:before, non_neg_integer()}
| {:order, :asc | :desc}
Expand Down Expand Up @@ -665,6 +666,9 @@ defmodule Algora.Contracts do
{:id, id}, query ->
from([c] in query, where: c.id == ^id)

{:contractor_id, contractor_id}, query ->
from([c] in query, where: c.contractor_id == ^contractor_id)

{:client_id, client_id}, query ->
from([c] in query, where: c.client_id == ^client_id)

Expand Down
44 changes: 11 additions & 33 deletions lib/algora/contracts/schemas/contract.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule Algora.Contracts.Contract do
alias Algora.Activities.Activity
alias Algora.Contracts.Contract
alias Algora.MoneyUtils
alias Algora.Validations

typed_schema "contracts" do
field :status, Ecto.Enum, values: [:draft, :active, :paid, :cancelled, :disputed]
Expand Down Expand Up @@ -78,51 +79,28 @@ defmodule Algora.Contracts.Contract do
:status,
:sequence_number,
:hourly_rate,
:hourly_rate_min,
:hourly_rate_max,
:hours_per_week,
:start_date,
:end_date,
:original_contract_id,
:client_id,
:contractor_id
])
|> validate_required([
:status,
:hourly_rate,
:hours_per_week,
:start_date,
:client_id,
:contractor_id
])
|> validate_required([:status, :hours_per_week, :client_id])
|> validate_number(:hours_per_week, greater_than: 0)
|> validate_number(:hourly_rate, greater_than: 0)
|> Validations.validate_money_positive(:hourly_rate)
|> foreign_key_constraint(:client_id)
|> foreign_key_constraint(:contractor_id)
|> generate_id()
|> put_original_contract_id()
end

def draft_changeset(contract, attrs) do
contract
|> cast(attrs, [
:status,
:sequence_number,
:hourly_rate_min,
:hourly_rate_max,
:hours_per_week,
:start_date,
:end_date,
:original_contract_id,
:client_id
])
|> validate_required([
:status,
:hourly_rate_min,
:hourly_rate_max,
:hours_per_week,
:start_date,
:client_id
])
|> validate_number(:hours_per_week, greater_than: 0)
|> foreign_key_constraint(:client_id)
|> generate_id()
def put_original_contract_id(changeset) do
case get_field(changeset, :original_contract_id) do
nil -> put_change(changeset, :original_contract_id, get_field(changeset, :id))
_existing -> changeset
end
end
end
2 changes: 1 addition & 1 deletion lib/algora/organizations/organizations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ defmodule Algora.Organizations do
)

contract_changeset =
Contract.draft_changeset(
Contract.changeset(
%Contract{},
Map.put(params.contract, :client_id, org.id)
)
Expand Down
2 changes: 1 addition & 1 deletion lib/algora_web/live/contract/modals/dispute_drawer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ defmodule AlgoraWeb.Contract.Modals.DisputeDrawer do
{Money.to_string!(@contract.amount_debited)}
</dd>
</div>
<div class="flex justify-between">
<div :if={@contract.start_date} class="flex justify-between">
<dt class="text-muted-foreground">Contract Period</dt>
<dd class="font-semibold">
{Calendar.strftime(@contract.start_date, "%b %d")} - {Calendar.strftime(
Expand Down
74 changes: 48 additions & 26 deletions lib/algora_web/live/contract/view_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,25 @@ defmodule AlgoraWeb.Contract.ViewLive do
<h1 class="text-2xl font-semibold">
Contract with {@contract.contractor.name}
</h1>
<p class="text-sm text-muted-foreground">
Started {Calendar.strftime(@contract.start_date, "%b %d, %Y")}
</p>
<%= if @contract.start_date do %>
<p class="text-sm text-muted-foreground">
Started {Calendar.strftime(@contract.start_date, "%b %d, %Y")}
</p>
<% else %>
<p class="text-sm text-muted-foreground">
Drafted on {Calendar.strftime(@contract.inserted_at, "%b %d, %Y")}
</p>
<% end %>
</div>
</div>
<div>
<.badge variant="success">Active</.badge>
</div>
<%= case @contract.status do %>
<% :draft -> %>
<.badge variant="warning">Draft</.badge>
<% :active -> %>
<.badge variant="success">Active</.badge>
<% _ -> %>
<.badge variant="destructive">Inactive</.badge>
<% end %>
</div>
<!-- Stats Grid -->
<div class="mt-8 grid grid-cols-4 gap-4">
Expand Down Expand Up @@ -84,14 +95,14 @@ defmodule AlgoraWeb.Contract.ViewLive do
</.card>
</div>
<!-- Tabs -->
<.tabs :let={builder} id="contract-tabs" default="payments" class="mt-8">
<.tabs :let={builder} id="contract-tabs" default="details" class="mt-8">
<.tabs_list class="flex w-full space-x-1 rounded-lg bg-muted p-1">
<.tabs_trigger builder={builder} value="payments" class="flex-1">
<.icon name="tabler-credit-card" class="mr-2 h-4 w-4" /> Payments
</.tabs_trigger>
<.tabs_trigger builder={builder} value="details" class="flex-1">
<.icon name="tabler-file-text" class="mr-2 h-4 w-4" /> Contract Details
</.tabs_trigger>
<.tabs_trigger builder={builder} value="payments" class="flex-1">
<.icon name="tabler-credit-card" class="mr-2 h-4 w-4" /> Payments
</.tabs_trigger>
<.tabs_trigger builder={builder} value="activity" class="flex-1">
<.icon name="tabler-history" class="mr-2 h-4 w-4" /> Activity
</.tabs_trigger>
Expand All @@ -106,7 +117,7 @@ defmodule AlgoraWeb.Contract.ViewLive do
</.card_description>
</.card_header>
<.card_content>
<div class="space-y-8">
<div :if={@contract.timesheet} class="space-y-8">
<%= for contract <- @contract_chain do %>
<%= case Contracts.get_payment_status(contract) do %>
<% {:pending_timesheet, contract} -> %>
Expand All @@ -119,7 +130,7 @@ defmodule AlgoraWeb.Contract.ViewLive do
<div class="font-medium">
Waiting for timesheet submission
</div>
<div class="text-sm text-muted-foreground">
<div :if={contract.start_date} class="text-sm text-muted-foreground">
{Calendar.strftime(contract.start_date, "%b %d")} - {Calendar.strftime(
contract.end_date,
"%b %d, %Y"
Expand All @@ -142,7 +153,7 @@ defmodule AlgoraWeb.Contract.ViewLive do
<div class="font-medium">
Ready to release payment for {contract.timesheet.hours_worked} hours
</div>
<div class="text-sm text-muted-foreground">
<div :if={contract.start_date} class="text-sm text-muted-foreground">
{Calendar.strftime(contract.start_date, "%b %d")} - {Calendar.strftime(
contract.end_date,
"%b %d, %Y"
Expand Down Expand Up @@ -464,19 +475,30 @@ defmodule AlgoraWeb.Contract.ViewLive do
thread = Chat.get_or_create_thread!(contract)
messages = thread.id |> Chat.list_messages() |> Repo.preload(:sender)

{:ok,
socket
|> assign(:contract, contract)
|> assign(:contract_chain, contract_chain)
|> assign(:has_more, length(contract_chain) >= page_size())
|> assign(:page_title, "Contract with #{contract.contractor.name}")
|> assign(:messages, messages)
|> assign(:thread, thread)
|> assign(:show_release_renew_modal, false)
|> assign(:show_release_modal, false)
|> assign(:show_dispute_modal, false)
|> assign(:fee_data, Contracts.calculate_fee_data(contract))
|> assign(:org_members, Organizations.list_org_members(contract.client))}
case socket.assigns[:current_user] do
nil ->
{:ok, redirect(socket, to: ~p"/auth/login?return_to=#{~p"/org/#{contract.client.handle}/contracts/#{id}"}")}

current_user ->
if current_user.id != contract.contractor_id and
not (socket.assigns.all_contexts |> Enum.map(& &1.id) |> Enum.member?(contract.client_id)) do
{:ok, raise(AlgoraWeb.NotFoundError)}
else
{:ok,
socket
|> assign(:contract, contract)
|> assign(:contract_chain, contract_chain)
|> assign(:has_more, length(contract_chain) >= page_size())
|> assign(:page_title, "Contract with #{contract.contractor.name}")
|> assign(:messages, messages)
|> assign(:thread, thread)
|> assign(:show_release_renew_modal, false)
|> assign(:show_release_modal, false)
|> assign(:show_dispute_modal, false)
|> assign(:fee_data, Contracts.calculate_fee_data(contract))
|> assign(:org_members, Organizations.list_org_members(contract.client))}
end
end
end

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