forked from shibao/cannery
		
	improve invites, record usage
This commit is contained in:
		@@ -4,23 +4,24 @@ defmodule CanneryWeb.Components.InviteCard do
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :component
 | 
			
		||||
  alias Cannery.Invites.Invite
 | 
			
		||||
  alias Cannery.Accounts.{Invite, Invites, User}
 | 
			
		||||
  alias CanneryWeb.Endpoint
 | 
			
		||||
 | 
			
		||||
  attr :invite, Invite, required: true
 | 
			
		||||
  attr :current_user, User, required: true
 | 
			
		||||
  slot(:inner_block)
 | 
			
		||||
  slot(:code_actions)
 | 
			
		||||
 | 
			
		||||
  def invite_card(assigns) do
 | 
			
		||||
    assigns = assigns |> assign_new(:code_actions, fn -> [] end)
 | 
			
		||||
  def invite_card(%{invite: invite, current_user: current_user} = assigns) do
 | 
			
		||||
    assigns =
 | 
			
		||||
      assigns
 | 
			
		||||
      |> assign(:use_count, Invites.get_use_count(invite, current_user))
 | 
			
		||||
      |> assign_new(:code_actions, fn -> [] end)
 | 
			
		||||
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <div
 | 
			
		||||
      id={"invite-#{@invite.id}"}
 | 
			
		||||
      class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4
 | 
			
		||||
        border border-gray-400 rounded-lg shadow-lg hover:shadow-md
 | 
			
		||||
        transition-all duration-300 ease-in-out"
 | 
			
		||||
    >
 | 
			
		||||
    <div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4
 | 
			
		||||
      border border-gray-400 rounded-lg shadow-lg hover:shadow-md
 | 
			
		||||
      transition-all duration-300 ease-in-out">
 | 
			
		||||
      <h1 class="title text-xl">
 | 
			
		||||
        <%= @invite.name %>
 | 
			
		||||
      </h1>
 | 
			
		||||
@@ -29,8 +30,8 @@ defmodule CanneryWeb.Components.InviteCard do
 | 
			
		||||
        <h2 class="title text-md">
 | 
			
		||||
          <%= if @invite.uses_left do %>
 | 
			
		||||
            <%= gettext(
 | 
			
		||||
              "Uses Left: %{uses_left}",
 | 
			
		||||
              uses_left: @invite.uses_left
 | 
			
		||||
              "Uses Left: %{uses_left_count}",
 | 
			
		||||
              uses_left_count: @invite.uses_left
 | 
			
		||||
            ) %>
 | 
			
		||||
          <% else %>
 | 
			
		||||
            <%= gettext("Uses Left: Unlimited") %>
 | 
			
		||||
@@ -47,6 +48,10 @@ defmodule CanneryWeb.Components.InviteCard do
 | 
			
		||||
        filename={@invite.name}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <h2 :if={@use_count != 0} class="title text-md">
 | 
			
		||||
        <%= gettext("Uses: %{uses_count}", uses_count: @use_count) %>
 | 
			
		||||
      </h2>
 | 
			
		||||
 | 
			
		||||
      <div class="flex flex-row flex-wrap justify-center items-center">
 | 
			
		||||
        <code
 | 
			
		||||
          id={"code-#{@invite.id}"}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
defmodule CanneryWeb.UserRegistrationController do
 | 
			
		||||
  use CanneryWeb, :controller
 | 
			
		||||
  import CanneryWeb.Gettext
 | 
			
		||||
  alias Cannery.{Accounts, Invites}
 | 
			
		||||
  alias Cannery.{Accounts, Accounts.Invites}
 | 
			
		||||
  alias CanneryWeb.{Endpoint, HomeLive}
 | 
			
		||||
 | 
			
		||||
  def new(conn, %{"invite" => invite_token}) do
 | 
			
		||||
    invite = Invites.get_invite_by_token(invite_token)
 | 
			
		||||
 | 
			
		||||
    if invite do
 | 
			
		||||
      conn |> render_new(invite)
 | 
			
		||||
    if Invites.valid_invite_token?(invite_token) do
 | 
			
		||||
      conn |> render_new(invite_token)
 | 
			
		||||
    else
 | 
			
		||||
      conn
 | 
			
		||||
      |> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
 | 
			
		||||
@@ -27,19 +25,17 @@ defmodule CanneryWeb.UserRegistrationController do
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # renders new user registration page
 | 
			
		||||
  defp render_new(conn, invite \\ nil) do
 | 
			
		||||
  defp render_new(conn, invite_token \\ nil) do
 | 
			
		||||
    render(conn, "new.html",
 | 
			
		||||
      changeset: Accounts.change_user_registration(),
 | 
			
		||||
      invite: invite,
 | 
			
		||||
      invite_token: invite_token,
 | 
			
		||||
      page_title: gettext("Register")
 | 
			
		||||
    )
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create(conn, %{"user" => %{"invite_token" => invite_token}} = attrs) do
 | 
			
		||||
    invite = Invites.get_invite_by_token(invite_token)
 | 
			
		||||
 | 
			
		||||
    if invite do
 | 
			
		||||
      conn |> create_user(attrs, invite)
 | 
			
		||||
    if Invites.valid_invite_token?(invite_token) do
 | 
			
		||||
      conn |> create_user(attrs, invite_token)
 | 
			
		||||
    else
 | 
			
		||||
      conn
 | 
			
		||||
      |> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
 | 
			
		||||
@@ -57,13 +53,9 @@ defmodule CanneryWeb.UserRegistrationController do
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp create_user(conn, %{"user" => user_params}, invite \\ nil) do
 | 
			
		||||
    case Accounts.register_user(user_params) do
 | 
			
		||||
  defp create_user(conn, %{"user" => user_params}, invite_token \\ nil) do
 | 
			
		||||
    case Accounts.register_user(user_params, invite_token) do
 | 
			
		||||
      {:ok, user} ->
 | 
			
		||||
        unless invite |> is_nil() do
 | 
			
		||||
          invite |> Invites.use_invite!()
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        Accounts.deliver_user_confirmation_instructions(
 | 
			
		||||
          user,
 | 
			
		||||
          &Routes.user_confirmation_url(conn, :confirm, &1)
 | 
			
		||||
@@ -73,8 +65,13 @@ defmodule CanneryWeb.UserRegistrationController do
 | 
			
		||||
        |> put_flash(:info, dgettext("prompts", "Please check your email to verify your account"))
 | 
			
		||||
        |> redirect(to: Routes.user_session_path(Endpoint, :new))
 | 
			
		||||
 | 
			
		||||
      {:error, :invalid_token} ->
 | 
			
		||||
        conn
 | 
			
		||||
        |> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
 | 
			
		||||
        |> redirect(to: Routes.live_path(Endpoint, HomeLive))
 | 
			
		||||
 | 
			
		||||
      {:error, %Ecto.Changeset{} = changeset} ->
 | 
			
		||||
        conn |> render("new.html", changeset: changeset, invite: invite)
 | 
			
		||||
        conn |> render("new.html", changeset: changeset, invite_token: invite_token)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
defmodule CanneryWeb.InviteLive.FormComponent do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Livecomponent that can update or create an Cannery.Invites.Invite
 | 
			
		||||
  Livecomponent that can update or create an Cannery.Accounts.Invite
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_component
 | 
			
		||||
  alias Cannery.{Accounts.User, Invites, Invites.Invite}
 | 
			
		||||
  alias Ecto.Changeset
 | 
			
		||||
  alias Cannery.Accounts.{Invite, Invites, User}
 | 
			
		||||
  alias Phoenix.LiveView.Socket
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
  @moduledoc """
 | 
			
		||||
  Liveview to show a Cannery.Invites.Invite index
 | 
			
		||||
  Liveview to show a Cannery.Accounts.Invite index
 | 
			
		||||
  """
 | 
			
		||||
 | 
			
		||||
  use CanneryWeb, :live_view
 | 
			
		||||
  import CanneryWeb.Components.{InviteCard, UserCard}
 | 
			
		||||
  alias Cannery.{Accounts, Invites, Invites.Invite}
 | 
			
		||||
  alias Cannery.Accounts
 | 
			
		||||
  alias Cannery.Accounts.{Invite, Invites}
 | 
			
		||||
  alias CanneryWeb.{Endpoint, HomeLive}
 | 
			
		||||
  alias Phoenix.LiveView.JS
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +18,7 @@ defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
      else
 | 
			
		||||
        prompt = dgettext("errors", "You are not authorized to view this page")
 | 
			
		||||
        return_to = Routes.live_path(Endpoint, HomeLive)
 | 
			
		||||
        socket |> put_flash(:error, prompt) |> push_navigate(to: return_to)
 | 
			
		||||
        socket |> put_flash(:error, prompt) |> push_redirect(to: return_to)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
@@ -50,7 +51,7 @@ defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
    %{name: invite_name} =
 | 
			
		||||
      id |> Invites.get_invite!(current_user) |> Invites.delete_invite!(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "%{name} deleted succesfully", name: invite_name)
 | 
			
		||||
    prompt = dgettext("prompts", "%{invite_name} deleted succesfully", invite_name: invite_name)
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_invites()}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -61,10 +62,12 @@ defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      Invites.get_invite!(id, current_user)
 | 
			
		||||
      |> Invites.update_invite(%{"uses_left" => nil}, current_user)
 | 
			
		||||
      |> Invites.update_invite(%{uses_left: nil}, current_user)
 | 
			
		||||
      |> case do
 | 
			
		||||
        {:ok, %{name: invite_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} updated succesfully", name: invite_name)
 | 
			
		||||
          prompt =
 | 
			
		||||
            dgettext("prompts", "%{invite_name} updated succesfully", invite_name: invite_name)
 | 
			
		||||
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> display_invites()
 | 
			
		||||
 | 
			
		||||
        {:error, changeset} ->
 | 
			
		||||
@@ -81,10 +84,12 @@ defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
      ) do
 | 
			
		||||
    socket =
 | 
			
		||||
      Invites.get_invite!(id, current_user)
 | 
			
		||||
      |> Invites.update_invite(%{"uses_left" => nil, "disabled_at" => nil}, current_user)
 | 
			
		||||
      |> Invites.update_invite(%{uses_left: nil, disabled_at: nil}, current_user)
 | 
			
		||||
      |> case do
 | 
			
		||||
        {:ok, %{name: invite_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} enabled succesfully", name: invite_name)
 | 
			
		||||
          prompt =
 | 
			
		||||
            dgettext("prompts", "%{invite_name} enabled succesfully", invite_name: invite_name)
 | 
			
		||||
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> display_invites()
 | 
			
		||||
 | 
			
		||||
        {:error, changeset} ->
 | 
			
		||||
@@ -103,10 +108,12 @@ defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
 | 
			
		||||
    socket =
 | 
			
		||||
      Invites.get_invite!(id, current_user)
 | 
			
		||||
      |> Invites.update_invite(%{"uses_left" => 0, "disabled_at" => now}, current_user)
 | 
			
		||||
      |> Invites.update_invite(%{uses_left: 0, disabled_at: now}, current_user)
 | 
			
		||||
      |> case do
 | 
			
		||||
        {:ok, %{name: invite_name}} ->
 | 
			
		||||
          prompt = dgettext("prompts", "%{name} disabled succesfully", name: invite_name)
 | 
			
		||||
          prompt =
 | 
			
		||||
            dgettext("prompts", "%{invite_name} disabled succesfully", invite_name: invite_name)
 | 
			
		||||
 | 
			
		||||
          socket |> put_flash(:info, prompt) |> display_invites()
 | 
			
		||||
 | 
			
		||||
        {:error, changeset} ->
 | 
			
		||||
@@ -130,7 +137,7 @@ defmodule CanneryWeb.InviteLive.Index do
 | 
			
		||||
      ) do
 | 
			
		||||
    %{email: user_email} = Accounts.get_user!(id) |> Accounts.delete_user!(current_user)
 | 
			
		||||
 | 
			
		||||
    prompt = dgettext("prompts", "%{name} deleted succesfully", name: user_email)
 | 
			
		||||
    prompt = dgettext("prompts", "%{user_email} deleted succesfully", user_email: user_email)
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket |> put_flash(:info, prompt) |> display_invites()}
 | 
			
		||||
  end
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
  <% end %>
 | 
			
		||||
 | 
			
		||||
  <div class="w-full flex flex-row flex-wrap justify-center items-stretch">
 | 
			
		||||
    <.invite_card :for={invite <- @invites} invite={invite}>
 | 
			
		||||
    <.invite_card :for={invite <- @invites} invite={invite} current_user={@current_user}>
 | 
			
		||||
      <:code_actions>
 | 
			
		||||
        <form phx-submit="copy_to_clipboard">
 | 
			
		||||
          <button
 | 
			
		||||
@@ -45,8 +45,8 @@
 | 
			
		||||
        phx-click="delete_invite"
 | 
			
		||||
        phx-value-id={invite.id}
 | 
			
		||||
        data-confirm={
 | 
			
		||||
          dgettext("prompts", "Are you sure you want to delete the invite for %{name}?",
 | 
			
		||||
            name: invite.name
 | 
			
		||||
          dgettext("prompts", "Are you sure you want to delete the invite for %{invite_name}?",
 | 
			
		||||
            invite_name: invite.name
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        data-qa={"delete-#{invite.id}"}
 | 
			
		||||
@@ -70,8 +70,8 @@
 | 
			
		||||
        phx-click="set_unlimited"
 | 
			
		||||
        phx-value-id={invite.id}
 | 
			
		||||
        data-confirm={
 | 
			
		||||
          dgettext("prompts", "Are you sure you want to make %{name} unlimited?",
 | 
			
		||||
            name: invite.name
 | 
			
		||||
          dgettext("prompts", "Are you sure you want to make %{invite_name} unlimited?",
 | 
			
		||||
            invite_name: invite.name
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      >
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ defmodule CanneryWeb.LiveHelpers do
 | 
			
		||||
        id="modal-content"
 | 
			
		||||
        class="fade-in-scale w-full max-w-3xl relative
 | 
			
		||||
          pointer-events-auto overflow-hidden
 | 
			
		||||
          px-8 py-4 sm:py-8 flex flex-col justify-center items-center
 | 
			
		||||
          px-8 py-4 sm:py-8
 | 
			
		||||
          flex flex-col justify-start items-center
 | 
			
		||||
          bg-white border-2 rounded-lg"
 | 
			
		||||
      >
 | 
			
		||||
 
 | 
			
		||||
@@ -9,14 +9,12 @@
 | 
			
		||||
    action={Routes.user_registration_path(@conn, :create)}
 | 
			
		||||
    class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
 | 
			
		||||
  >
 | 
			
		||||
    <div :if={@changeset.action && not @changeset.valid?()} class="alert alert-danger col-span-3">
 | 
			
		||||
      <p>
 | 
			
		||||
        <%= dgettext("errors", "Oops, something went wrong! Please check the errors below.") %>
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
    <p :if={@changeset.action && not @changeset.valid?()} class="alert alert-danger col-span-3">
 | 
			
		||||
      <%= dgettext("errors", "Oops, something went wrong! Please check the errors below.") %>
 | 
			
		||||
    </p>
 | 
			
		||||
 | 
			
		||||
    <%= if @invite do %>
 | 
			
		||||
      <%= hidden_input(f, :invite_token, value: @invite.token) %>
 | 
			
		||||
    <%= if @invite_token do %>
 | 
			
		||||
      <%= hidden_input(f, :invite_token, value: @invite_token) %>
 | 
			
		||||
    <% end %>
 | 
			
		||||
 | 
			
		||||
    <%= label(f, :email, class: "title text-lg text-primary-600") %>
 | 
			
		||||
@@ -31,7 +29,7 @@
 | 
			
		||||
    <%= select(
 | 
			
		||||
      f,
 | 
			
		||||
      :locale,
 | 
			
		||||
      [{gettext("English"), "en_US"}, {gettext("German"), "de"}, {gettext("French"), "fr"}],
 | 
			
		||||
      [{gettext("English"), "en_US"}],
 | 
			
		||||
      class: "input input-primary col-span-2"
 | 
			
		||||
    ) %>
 | 
			
		||||
    <%= error_tag(f, :locale) %>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user