rename to memex

This commit is contained in:
2023-02-26 00:47:51 -05:00
parent 9b4837a044
commit bc2c936480
126 changed files with 791 additions and 791 deletions

View File

@ -0,0 +1,89 @@
defmodule MemexWeb.InviteLive.FormComponent do
@moduledoc """
Livecomponent that can update or create an Memex.Accounts.Invite
"""
use MemexWeb, :live_component
alias Ecto.Changeset
alias Memex.Accounts.{Invite, Invites, User}
alias Phoenix.LiveView.Socket
@impl true
@spec update(
%{:invite => Invite.t(), :current_user => User.t(), optional(any) => any},
Socket.t()
) :: {:ok, Socket.t()}
def update(%{invite: _invite} = assigns, socket) do
{:ok, socket |> assign(assigns) |> assign_changeset(%{})}
end
@impl true
def handle_event("validate", %{"invite" => invite_params}, socket) do
{:noreply, socket |> assign_changeset(invite_params)}
end
def handle_event("save", %{"invite" => invite_params}, %{assigns: %{action: action}} = socket) do
save_invite(socket, action, invite_params)
end
defp assign_changeset(
%{assigns: %{action: action, current_user: user, invite: invite}} = socket,
invite_params
) do
changeset_action =
case action do
:new -> :insert
:edit -> :update
end
changeset =
case action do
:new -> Invite.create_changeset(user, "example_token", invite_params)
:edit -> invite |> Invite.update_changeset(invite_params)
end
changeset =
case changeset |> Changeset.apply_action(changeset_action) do
{:ok, _data} -> changeset
{:error, changeset} -> changeset
end
socket |> assign(:changeset, changeset)
end
defp save_invite(
%{assigns: %{current_user: current_user, invite: invite, return_to: return_to}} = socket,
:edit,
invite_params
) do
socket =
case invite |> Invites.update_invite(invite_params, current_user) do
{:ok, %{name: invite_name}} ->
prompt = dgettext("prompts", "%{name} updated successfully", name: invite_name)
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
{:error, %Changeset{} = changeset} ->
socket |> assign(:changeset, changeset)
end
{:noreply, socket}
end
defp save_invite(
%{assigns: %{current_user: current_user, return_to: return_to}} = socket,
:new,
invite_params
) do
socket =
case current_user |> Invites.create_invite(invite_params) do
{:ok, %{name: invite_name}} ->
prompt = dgettext("prompts", "%{name} created successfully", name: invite_name)
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
{:error, %Changeset{} = changeset} ->
socket |> assign(changeset: changeset)
end
{:noreply, socket}
end
end

View File

@ -0,0 +1,37 @@
<div>
<h2 class="mb-8 text-center title text-xl text-primary-600">
<%= @title %>
</h2>
<.form
:let={f}
for={@changeset}
id="invite-form"
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
<div
:if={@changeset.action && not @changeset.valid?()}
class="invalid-feedback col-span-3 text-center"
>
<%= changeset_errors(@changeset) %>
</div>
<%= label(f, :name, gettext("Name"), class: "title text-lg text-primary-600") %>
<%= text_input(f, :name, class: "input input-primary col-span-2") %>
<%= error_tag(f, :name, "col-span-3") %>
<%= label(f, :uses_left, gettext("Uses left"), class: "title text-lg text-primary-600") %>
<%= number_input(f, :uses_left, min: 0, class: "input input-primary col-span-2") %>
<%= error_tag(f, :uses_left, "col-span-3") %>
<span class="col-span-3 text-primary-400 italic text-center">
<%= gettext(~s/Leave "Uses left" blank to make invite unlimited/) %>
</span>
<%= submit(dgettext("actions", "Save"),
class: "mx-auto btn btn-primary col-span-3",
phx_disable_with: dgettext("prompts", "Saving...")
) %>
</.form>
</div>

View File

@ -0,0 +1,157 @@
defmodule MemexWeb.InviteLive.Index do
@moduledoc """
Liveview to show a Memex.Accounts.Invite index
"""
use MemexWeb, :live_view
import MemexWeb.Components.{InviteCard, UserCard}
alias Memex.Accounts
alias Memex.Accounts.{Invite, Invites}
alias MemexWeb.{Endpoint, HomeLive}
alias Phoenix.LiveView.JS
@impl true
def mount(_params, _session, %{assigns: %{current_user: current_user}} = socket) do
socket =
if current_user |> Map.get(:role) == :admin do
socket |> display_invites()
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_redirect(to: return_to)
end
{:ok, socket}
end
@impl true
def handle_params(params, _url, %{assigns: %{live_action: live_action}} = socket) do
{:noreply, socket |> apply_action(live_action, params)}
end
defp apply_action(%{assigns: %{current_user: current_user}} = socket, :edit, %{"id" => id}) do
socket
|> assign(page_title: gettext("Edit Invite"), invite: Invites.get_invite!(id, current_user))
end
defp apply_action(socket, :new, _params) do
socket |> assign(page_title: gettext("New Invite"), invite: %Invite{})
end
defp apply_action(socket, :index, _params) do
socket |> assign(page_title: gettext("Invites"), invite: nil)
end
@impl true
def handle_event(
"delete_invite",
%{"id" => id},
%{assigns: %{current_user: current_user}} = socket
) do
%{name: invite_name} =
id |> Invites.get_invite!(current_user) |> Invites.delete_invite!(current_user)
prompt = dgettext("prompts", "%{invite_name} deleted succesfully", invite_name: invite_name)
{:noreply, socket |> put_flash(:info, prompt) |> display_invites()}
end
def handle_event(
"set_unlimited",
%{"id" => id},
%{assigns: %{current_user: current_user}} = socket
) do
socket =
Invites.get_invite!(id, current_user)
|> Invites.update_invite(%{uses_left: nil}, current_user)
|> case do
{:ok, %{name: invite_name}} ->
prompt =
dgettext("prompts", "%{invite_name} updated succesfully", invite_name: invite_name)
socket |> put_flash(:info, prompt) |> display_invites()
{:error, changeset} ->
socket |> put_flash(:error, changeset |> changeset_errors())
end
{:noreply, socket}
end
def handle_event(
"enable_invite",
%{"id" => id},
%{assigns: %{current_user: current_user}} = socket
) do
socket =
Invites.get_invite!(id, current_user)
|> Invites.update_invite(%{uses_left: nil, disabled_at: nil}, current_user)
|> case do
{:ok, %{name: invite_name}} ->
prompt =
dgettext("prompts", "%{invite_name} enabled succesfully", invite_name: invite_name)
socket |> put_flash(:info, prompt) |> display_invites()
{:error, changeset} ->
socket |> put_flash(:error, changeset |> changeset_errors())
end
{:noreply, socket}
end
def handle_event(
"disable_invite",
%{"id" => id},
%{assigns: %{current_user: current_user}} = socket
) do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
socket =
Invites.get_invite!(id, current_user)
|> Invites.update_invite(%{uses_left: 0, disabled_at: now}, current_user)
|> case do
{:ok, %{name: invite_name}} ->
prompt =
dgettext("prompts", "%{invite_name} disabled succesfully", invite_name: invite_name)
socket |> put_flash(:info, prompt) |> display_invites()
{:error, changeset} ->
socket |> put_flash(:error, changeset |> changeset_errors())
end
{:noreply, socket}
end
@impl true
def handle_event("copy_to_clipboard", _params, socket) do
prompt = dgettext("prompts", "Copied to clipboard")
{:noreply, socket |> put_flash(:info, prompt)}
end
@impl true
def handle_event(
"delete_user",
%{"id" => id},
%{assigns: %{current_user: current_user}} = socket
) do
%{email: user_email} = Accounts.get_user!(id) |> Accounts.delete_user!(current_user)
prompt = dgettext("prompts", "%{user_email} deleted succesfully", user_email: user_email)
{:noreply, socket |> put_flash(:info, prompt) |> display_invites()}
end
defp display_invites(%{assigns: %{current_user: current_user}} = socket) do
invites = Invites.list_invites(current_user)
all_users = Accounts.list_all_users_by_role(current_user)
admins =
all_users
|> Map.get(:admin, [])
|> Enum.reject(fn %{id: user_id} -> user_id == current_user.id end)
users = all_users |> Map.get(:user, [])
socket |> assign(invites: invites, admins: admins, users: users)
end
end

View File

@ -0,0 +1,149 @@
<div class="w-full flex flex-col space-y-8 justify-center items-center">
<h1 class="title text-2xl title-primary-500">
<%= gettext("Invites") %>
</h1>
<%= if @invites |> Enum.empty?() do %>
<h1 class="title text-xl text-primary-600">
<%= gettext("No invites 😔") %>
</h1>
<.link patch={Routes.invite_index_path(Endpoint, :new)} class="btn btn-primary">
<%= dgettext("actions", "Invite someone new!") %>
</.link>
<% else %>
<.link patch={Routes.invite_index_path(Endpoint, :new)} class="btn btn-primary">
<%= dgettext("actions", "Create Invite") %>
</.link>
<% end %>
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
<.invite_card :for={invite <- @invites} invite={invite} current_user={@current_user}>
<:code_actions>
<form phx-submit="copy_to_clipboard">
<button
type="submit"
class="mx-2 my-1 btn btn-primary"
phx-click={JS.dispatch("memex:clipcopy", to: "#code-#{invite.id}")}
>
<%= dgettext("actions", "Copy to clipboard") %>
</button>
</form>
</:code_actions>
<.link
patch={Routes.invite_index_path(Endpoint, :edit, invite)}
class="text-primary-600 link"
data-qa={"edit-#{invite.id}"}
>
<i class="fa-fw fa-lg fas fa-edit"></i>
</.link>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete_invite"
phx-value-id={invite.id}
data-confirm={
dgettext("prompts", "Are you sure you want to delete the invite for %{invite_name}?",
invite_name: invite.name
)
}
data-qa={"delete-#{invite.id}"}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
<a
href="#"
class="btn btn-primary"
phx-click={if invite.disabled_at, do: "enable_invite", else: "disable_invite"}
phx-value-id={invite.id}
>
<%= if invite.disabled_at, do: gettext("Enable"), else: gettext("Disable") %>
</a>
<a
:if={invite.disabled_at |> is_nil() and not (invite.uses_left |> is_nil())}
href="#"
class="btn btn-primary"
phx-click="set_unlimited"
phx-value-id={invite.id}
data-confirm={
dgettext("prompts", "Are you sure you want to make %{invite_name} unlimited?",
invite_name: invite.name
)
}
>
<%= gettext("Set Unlimited") %>
</a>
</.invite_card>
</div>
<%= unless @admins |> Enum.empty?() do %>
<hr class="hr" />
<h1 class="title text-2xl text-primary-600">
<%= gettext("Admins") %>
</h1>
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
<.user_card :for={admin <- @admins} user={admin}>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete_user"
phx-value-id={admin.id}
data-confirm={
dgettext(
"prompts",
"Are you sure you want to delete %{email}? This action is permanent!",
email: admin.email
)
}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</.user_card>
</div>
<% end %>
<%= unless @users |> Enum.empty?() do %>
<hr class="hr" />
<h1 class="title text-2xl text-primary-600">
<%= gettext("Users") %>
</h1>
<div class="w-full flex flex-row flex-wrap justify-center items-stretch">
<.user_card :for={user <- @users} user={user}>
<.link
href="#"
class="text-primary-600 link"
phx-click="delete_user"
phx-value-id={user.id}
data-confirm={
dgettext(
"prompts",
"Are you sure you want to delete %{email}? This action is permanent!",
email: user.email
)
}
>
<i class="fa-fw fa-lg fas fa-trash"></i>
</.link>
</.user_card>
</div>
<% end %>
</div>
<.modal :if={@live_action in [:new, :edit]} return_to={Routes.invite_index_path(Endpoint, :index)}>
<.live_component
module={MemexWeb.InviteLive.FormComponent}
id={@invite.id || :new}
title={@page_title}
action={@live_action}
invite={@invite}
return_to={Routes.invite_index_path(Endpoint, :index)}
current_user={@current_user}
/>
</.modal>