diff --git a/.iex.exs b/.iex.exs index 0f06743e0..e346ca507 100644 --- a/.iex.exs +++ b/.iex.exs @@ -7,6 +7,7 @@ alias Algora.Accounts.Identity alias Algora.Accounts.User alias Algora.Admin alias Algora.Admin.Migration +alias Algora.Analytics alias Algora.Bounties alias Algora.Bounties.Claim alias Algora.Contracts diff --git a/lib/algora/admin/mainthing/mainthing.ex b/lib/algora/admin/mainthing/mainthing.ex new file mode 100644 index 000000000..2fbdf8aaf --- /dev/null +++ b/lib/algora/admin/mainthing/mainthing.ex @@ -0,0 +1,42 @@ +defmodule Algora.Admin.Mainthings do + @moduledoc false + + import Ecto.Query + + alias Algora.Admin.Mainthings.Mainthing + alias Algora.Repo + + @doc """ + Gets the latest mainthing entry. + """ + def get_latest do + Mainthing + |> last(:inserted_at) + |> Repo.one() + end + + @doc """ + Creates a new mainthing entry. + """ + def create(attrs \\ %{}) do + %Mainthing{} + |> Mainthing.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a mainthing entry. + """ + def update(%Mainthing{} = mainthing, attrs) do + mainthing + |> Mainthing.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a mainthing entry. + """ + def delete(%Mainthing{} = mainthing) do + Repo.delete(mainthing) + end +end diff --git a/lib/algora/admin/mainthing/schemas/mainthing.ex b/lib/algora/admin/mainthing/schemas/mainthing.ex new file mode 100644 index 000000000..ec9cda85e --- /dev/null +++ b/lib/algora/admin/mainthing/schemas/mainthing.ex @@ -0,0 +1,19 @@ +defmodule Algora.Admin.Mainthings.Mainthing do + @moduledoc false + use Algora.Schema + + import Ecto.Changeset + + typed_schema "mainthings" do + field :content, :string, null: false + + timestamps() + end + + def changeset(mainthing, attrs) do + mainthing + |> cast(attrs, [:content]) + |> validate_required([:content]) + |> generate_id() + end +end diff --git a/lib/algora/analytics/analytics.ex b/lib/algora/analytics/analytics.ex index 1d5f75144..d61f92fcd 100644 --- a/lib/algora/analytics/analytics.ex +++ b/lib/algora/analytics/analytics.ex @@ -3,7 +3,7 @@ defmodule Algora.Analytics do import Ecto.Query alias Algora.Accounts.User - alias Algora.Contracts.Contract + alias Algora.Bounties.Bounty alias Algora.Repo require Algora.SQL @@ -42,41 +42,45 @@ defmodule Algora.Analytics do } contracts_query = - from u in Contract, - where: u.inserted_at >= ^previous_period_start, + from b in Bounty, + where: b.inserted_at >= ^previous_period_start, select: %{ - count_current: u.id |> count() |> filter(u.inserted_at < ^from and u.inserted_at >= ^period_start), + count_current: b.id |> count() |> filter(b.inserted_at < ^from and b.inserted_at >= ^period_start), count_previous: - u.id |> count() |> filter(u.inserted_at < ^period_start and u.inserted_at >= ^previous_period_start), + b.id |> count() |> filter(b.inserted_at < ^period_start and b.inserted_at >= ^previous_period_start), success_current: - u.id + b.id |> count() |> filter( - u.inserted_at < ^from and u.inserted_at >= ^period_start and (u.status == :active or u.status == :paid) + b.inserted_at < ^from and b.inserted_at >= ^period_start and (b.status == :open or b.status == :paid) ), success_previous: - u.id + b.id |> count() |> filter( - u.inserted_at < ^period_start and u.inserted_at >= ^previous_period_start and - (u.status == :active or u.status == :paid) + b.inserted_at < ^period_start and b.inserted_at >= ^previous_period_start and + (b.status == :open or b.status == :paid) ) } companies_query = from u in User, - where: u.inserted_at >= ^period_start and u.type == :organization, - right_join: c in Contract, - on: c.client_id == u.id, - group_by: u.id, + where: u.inserted_at >= ^period_start, + where: u.type == :organization, + where: u.featured, + left_join: b in Bounty, + on: b.owner_id == u.id, + distinct: [u.id], + group_by: [u.id, b.id], + order_by: [desc: b.inserted_at], select: %{ id: u.id, name: u.name, handle: u.handle, joined_at: u.inserted_at, - total_contracts: c.id |> count() |> filter(c.inserted_at >= ^period_start), - successful_contracts: - c.id |> count() |> filter(c.status == :active or (c.status == :paid and c.inserted_at >= ^period_start)), + total_bounties: b.id |> count() |> filter(b.inserted_at >= ^period_start), + successful_bounties: + b.id |> count() |> filter(b.status == :open or (b.status == :paid and b.inserted_at >= ^period_start)), last_active_at: u.updated_at, avatar_url: u.avatar_url } @@ -112,15 +116,15 @@ defmodule Algora.Analytics do avg_time_to_fill: 0.0, time_to_fill_change: -0.0, time_to_fill_trend: :down, - contract_success_rate: current_success_rate, - previous_contract_success_rate: previous_success_rate, + bounty_success_rate: current_success_rate, + previous_bounty_success_rate: previous_success_rate, success_rate_change: current_success_rate - previous_success_rate, success_rate_trend: calculate_trend(current_success_rate, previous_success_rate), companies: Enum.map(companies, fn company -> Map.merge(company, %{ - success_rate: calculate_success_rate(company.successful_contracts, company.total_contracts), - status: if(company.successful_contracts > 0, do: :active, else: :inactive) + success_rate: calculate_success_rate(company.successful_bounties, company.total_bounties), + status: if(company.successful_bounties > 0, do: :active, else: :inactive) }) end) }} diff --git a/lib/algora_web/components/core_components.ex b/lib/algora_web/components/core_components.ex index 6b957be0e..10e9402cd 100644 --- a/lib/algora_web/components/core_components.ex +++ b/lib/algora_web/components/core_components.ex @@ -236,6 +236,14 @@ defmodule AlgoraWeb.CoreComponents do + <:link :if={@current_user.is_admin} href={~p"/admin"}> +
+
+ <.icon name="tabler-adjustments-alt" class="h-5 w-5" /> +
+
Admin
+
+ <:link href={~p"/auth/logout"}>
diff --git a/lib/algora_web/components/layouts/user.html.heex b/lib/algora_web/components/layouts/user.html.heex index 92d81d3cf..db10b8205 100644 --- a/lib/algora_web/components/layouts/user.html.heex +++ b/lib/algora_web/components/layouts/user.html.heex @@ -82,54 +82,58 @@ <.logo class="h-8 w-auto text-white" />
@@ -137,7 +141,7 @@