Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create organizations slug #393

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 5 additions & 1 deletion lib/atomic/activities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ defmodule Atomic.Activities do
alias Atomic.Activities.Enrollment
alias Atomic.Activities.Session
alias Atomic.Activities.Speaker
alias Atomic.Organizations

@doc """
Returns the list of activities.
Expand Down Expand Up @@ -100,8 +101,11 @@ defmodule Atomic.Activities do
"""
def get_session_organizations!(session, preloads \\ [:departments]) do
MarioRodrigues10 marked this conversation as resolved.
Show resolved Hide resolved
Repo.preload(session, preloads)
|> Map.get(:departments, [])
|> Map.get(:departments)
|> Enum.map(& &1.organization_id)
|> Enum.map(fn id ->
Organizations.get_organization!(id).handle
end)
end

@doc """
Expand Down
27 changes: 26 additions & 1 deletion lib/atomic/organizations.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ defmodule Atomic.Organizations do
|> Repo.update()
end

@doc """
Gets an organization by handle.

## Examples
iex> get_organization_by_handle("foo_bar")
%Organization{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
%Organization{}
%Organization{}

iex> get_organization_by_handle("unknown")
nil

"""
def get_organization_by_handle(handle) when is_binary(handle) do
Repo.get_by(Organization, handle: handle)
end

@doc """
Returns an `%Ecto.Changeset{}` for changing the organization handle.

## Examples
iex> change_organization_handle(organization)
%Ecto.Changeset{data: %Organization{}}
"""
def change_organization_handle(organization, attrs \\ %{}) do
Organization.handle_changeset(organization, attrs)
end

@doc """
Updates an organization card image.

Expand Down Expand Up @@ -213,7 +238,7 @@ defmodule Atomic.Organizations do
nil

"""
def get_role(user_id, organization_id) do
def get_role(user_id, organization_id) when is_binary(user_id) and is_binary(organization_id) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Membership
|> where([m], m.user_id == ^user_id and m.organization_id == ^organization_id)
|> Repo.one()
Expand Down
30 changes: 29 additions & 1 deletion lib/atomic/organizations/organization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ defmodule Atomic.Organizations.Organization do
alias Atomic.Organizations.{Announcement, Board, Card, Department, Membership, Partner}
alias Atomic.Uploaders

@required_fields ~w(name description)a
@required_fields ~w(name handle description)a
ruilopesm marked this conversation as resolved.
Show resolved Hide resolved
@optional_fields ~w(card_image logo)a

schema "organizations" do
field :name, :string
field :description, :string
field :card_image, Uploaders.Card.Type
field :logo, Uploaders.Logo.Type
field :handle, :string

has_many :departments, Department,
on_replace: :delete_if_exists,
Expand Down Expand Up @@ -53,6 +54,7 @@ defmodule Atomic.Organizations.Organization do
|> cast_embed(:card, with: &Card.changeset/2)
|> cast_attachments(attrs, [:card_image, :logo])
|> validate_required(@required_fields)
|> validate_handle()
|> unique_constraint(:name)
end

Expand All @@ -66,4 +68,30 @@ defmodule Atomic.Organizations.Organization do
organization
|> cast_attachments(attrs, [:logo])
end

@doc """
An organization changeset for changing the handle.
It requires the handle to change otherwise an error is added.
"""
def handle_changeset(organization, attrs) do
organization
|> cast(attrs, [:handle])
|> validate_handle()
|> case do
%{changes: %{handle: _}} = changeset -> changeset
%{} = changeset -> add_error(changeset, :handle, "did not change")
end
end

defp validate_handle(changeset) do
changeset
|> validate_required([:handle])
|> validate_format(:handle, ~r/^[a-zA-Z0-9_.]+$/,
message:
gettext("must only contain alphanumeric characters, numbers, underscores and periods")
)
|> validate_length(:handle, min: 3, max: 30)
|> unsafe_validate_unique(:handle, Atomic.Repo)
|> unique_constraint(:handle)
end
end
23 changes: 13 additions & 10 deletions lib/atomic_web/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ defmodule AtomicWeb.Config do
def pages(conn, current_organization, current_user) do
if current_organization do
if current_user.role in [:admin] or
Organizations.get_role(current_user.id, current_organization.id) in [:admin, :owner] do
Organizations.get_role(current_user.id, current_organization.id) in [
:admin,
:owner
] do
admin_pages(conn, current_organization, current_user)
else
user_pages(conn, current_organization, current_user)
Expand Down Expand Up @@ -50,35 +53,35 @@ defmodule AtomicWeb.Config do
key: :activities,
title: "Activities",
icon: :academic_cap,
url: Routes.activity_index_path(conn, :index, current_organization.id),
url: Routes.activity_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :departments,
title: "Departments",
icon: :cube,
url: Routes.department_index_path(conn, :index, current_organization.id),
url: Routes.department_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :partners,
title: "Partners",
icon: :user_group,
url: Routes.partner_index_path(conn, :index, current_organization.id),
url: Routes.partner_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :memberships,
title: "Memberships",
icon: :user_add,
url: Routes.membership_index_path(conn, :index, current_organization.id),
url: Routes.membership_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :board,
title: "Board",
icon: :users,
url: Routes.board_index_path(conn, :index, current_organization.id),
url: Routes.board_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
Expand Down Expand Up @@ -132,28 +135,28 @@ defmodule AtomicWeb.Config do
key: :activities,
title: "Activities",
icon: :academic_cap,
url: Routes.activity_index_path(conn, :index, current_organization.id),
url: Routes.activity_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :departments,
title: "Departments",
icon: :cube,
url: Routes.department_index_path(conn, :index, current_organization.id),
url: Routes.department_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :partners,
title: "Partners",
icon: :user_group,
url: Routes.partner_index_path(conn, :index, current_organization.id),
url: Routes.partner_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
key: :board,
title: "Board",
icon: :users,
url: Routes.board_index_path(conn, :index, current_organization.id),
url: Routes.board_index_path(conn, :index, current_organization.handle),
tabs: []
},
%{
Expand Down
7 changes: 4 additions & 3 deletions lib/atomic_web/live/activity_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ defmodule AtomicWeb.ActivityLive.Index do

@impl true
def mount(params, _session, socket) do
{:ok, assign(socket, :sessions, list_sessions(params["organization_id"]))}
organization = Organizations.get_organization_by_handle(params["handle"])
{:ok, assign(socket, :sessions, list_sessions(organization.id))}
end

@impl true
def handle_params(params, _url, socket) do
entries = [
%{
name: gettext("Activities"),
route: Routes.activity_index_path(socket, :index, params["organization_id"])
route: Routes.activity_index_path(socket, :index, params["handle"])
}
]

Expand Down Expand Up @@ -60,7 +61,7 @@ defmodule AtomicWeb.ActivityLive.Index do
end

defp apply_action(socket, :index, params) do
organization = Organizations.get_organization!(params["organization_id"])
organization = Organizations.get_organization_by_handle(params["handle"])

socket
|> assign(:page_title, "#{organization.name}'s Activities")
Expand Down
6 changes: 3 additions & 3 deletions lib/atomic_web/live/activity_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<%= if not @empty and @has_permissions do %>
<div class="hidden lg:border-orange-500 lg:block">
<%= live_patch("+ New Activity",
to: Routes.activity_new_path(@socket, :new, @current_organization),
to: Routes.activity_new_path(@socket, :new, @current_organization.handle),
class: "border-2 rounded-md bg-white text-lg border-orange-500 py-2 px-3.5 text-sm font-medium text-orange-500 shadow-sm hover:bg-orange-500 hover:text-white"
) %>
</div>
Expand All @@ -32,15 +32,15 @@
</div>
<%= if @empty do %>
<div class="mt-32">
<.empty_state id="empty_state" url={Routes.activity_new_path(@socket, :new, @current_organization)} plural_placeholder="activities" placeholder="activity" />
<.empty_state id="empty_state" url={Routes.activity_new_path(@socket, :new, @current_organization.handle)} plural_placeholder="activities" placeholder="activity" />
</div>
<% else %>
<div>
<div class="overflow-hidden bg-white">
<ul role="list" class="divide-y divide-zinc-200">
<%= for session <- @sessions do %>
<li>
<%= live_redirect to: Routes.activity_show_path(@socket, :show, @current_organization, session), class: "block hover:bg-zinc-50", replace: false do %>
<%= live_redirect to: Routes.activity_show_path(@socket, :show, @current_organization.handle, session), class: "block hover:bg-zinc-50", replace: false do %>
<div class="px-4 py-4 lg:px-6">
<div class="flex items-center justify-between">
<p class="truncate text-sm font-medium text-zinc-900">
Expand Down
16 changes: 9 additions & 7 deletions lib/atomic_web/live/activity_live/show.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,37 @@ defmodule AtomicWeb.ActivityLive.Show do
alias AtomicWeb.MismatchError

@impl true
def mount(%{"id" => id}, _session, socket) do
def mount(%{"handle" => handle, "id" => id}, _session, socket) do
if connected?(socket) do
Activities.subscribe("new_enrollment")
Activities.subscribe("deleted_enrollment")
end

{:ok, assign(socket, :id, id)}
{:ok,
socket
|> assign(:id, id)
|> assign(:handle, handle)}
end

@impl true
def handle_params(%{"organization_id" => organization_id, "id" => id}, _, socket) do
def handle_params(%{"handle" => handle, "id" => id}, _, socket) do
session = Activities.get_session!(id, [:activity, :speakers, :departments])

activity = Activities.get_activity!(session.activity_id, [:sessions])

organizations = Activities.get_session_organizations!(session, [:departments])

entries = [
%{
name: gettext("Activities"),
route: Routes.activity_index_path(socket, :index, organization_id)
route: Routes.activity_index_path(socket, :index, handle)
},
%{
name: activity.title,
route: Routes.activity_show_path(socket, :show, organization_id, session.id)
route: Routes.activity_show_path(socket, :show, handle, session.id)
}
]

if organization_id in organizations do
if handle in organizations do
{:noreply,
socket
|> assign(
Expand Down
14 changes: 4 additions & 10 deletions lib/atomic_web/live/activity_live/show.html.heex
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
<%= if @live_action in [:edit] do %>
<.modal return_to={Routes.activity_show_path(@socket, :show, @current_organization, @activity)}>
<.live_component module={AtomicWeb.ActivityLive.FormComponent} id={@activity.id} title={@page_title} action={@live_action} activity={@activity} return_to={Routes.activity_show_path(@socket, :show, @current_organization, @activity)} />
</.modal>
<% end %>

<%= if @live_action in [:edit] do %>
<.modal return_to={Routes.activity_show_path(@socket, :show, @current_organization, @activity)}>
<.live_component module={AtomicWeb.ActivityLive.FormComponent} id={@activity.id} organization={@current_organization} title={@page_title} action={@live_action} activity={@activity} return_to={Routes.activity_show_path(@socket, :show, @current_organization, @activity)} />
<.modal return_to={Routes.activity_show_path(@socket, :show, @current_organization.handle, @activity)}>
<.live_component module={AtomicWeb.ActivityLive.FormComponent} id={@activity.id} title={@page_title} action={@live_action} activity={@activity} return_to={Routes.activity_show_path(@socket, :show, @current_organization.handle, @activity)} />
</.modal>
<% end %>

Expand Down Expand Up @@ -117,7 +111,7 @@
<%= Accounts.extract_initials(speaker.name) %>
</span>
</span>
<%= live_redirect to: Routes.speaker_show_path(@socket, :show, @current_organization, speaker), class: "text-md text-blue-500" do %>
<%= live_redirect to: Routes.speaker_show_path(@socket, :show, @current_organization.handle, speaker), class: "text-md text-blue-500" do %>
<%= Accounts.extract_first_last_name(speaker.name) %>
<% end %>
</div>
Expand Down Expand Up @@ -169,7 +163,7 @@

<div class="flex flex-row space-x-1">
<%= if @current_user.role in [:admin] or Organizations.is_admin?(@current_user, @session.departments) do %>
<%= live_patch to: Routes.activity_edit_path(@socket, :edit, @activity, @current_organization), class: "button" do %>
<%= live_patch to: Routes.activity_edit_path(@socket, :edit, @current_organization.handle, @activity), class: "button" do %>
<div type="button" class="inline-flex justify-center py-2 px-4 w-fit text-sm font-medium text-gray-700 bg-white rounded-md border border-gray-300 shadow-sm hover:bg-gray-50" id="sort-menu-button" aria-expanded="false" aria-haspopup="true">
<Heroicons.Solid.pencil class="mr-3 w-5 h-5 text-gray-400" /> Edit
</div>
Expand Down
12 changes: 7 additions & 5 deletions lib/atomic_web/live/announcement_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ defmodule AtomicWeb.AnnouncementLive.Index do
alias Atomic.Organizations.Announcement

@impl true
def mount(%{"organization_id" => organization_id}, _session, socket) do
def mount(%{"handle" => handle}, _session, socket) do
organization = Organizations.get_organization_by_handle(handle)

socket =
socket
|> assign(:organization, Organizations.get_organization!(organization_id))
|> assign(:announcements, list_announcements(organization_id))
|> assign(:organization, organization)
|> assign(:announcements, list_announcements(organization.id))

{:ok, socket}
end
Expand All @@ -23,7 +25,7 @@ defmodule AtomicWeb.AnnouncementLive.Index do
entries = [
%{
name: gettext("Announcement"),
route: Routes.announcement_index_path(socket, :index, params["organization_id"])
route: Routes.announcement_index_path(socket, :index, params["handle"])
}
]

Expand Down Expand Up @@ -65,7 +67,7 @@ defmodule AtomicWeb.AnnouncementLive.Index do
end

defp apply_action(socket, :index, params) do
organization = Organizations.get_organization!(params["organization_id"])
organization = Organizations.get_organization_by_handle(params["handle"])

socket
|> assign(:page_title, "#{organization.name}'s Announcement")
Expand Down
6 changes: 3 additions & 3 deletions lib/atomic_web/live/announcement_live/index.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<%= if not @empty and @has_permissions do %>
<div class="hidden lg:border-orange-500 lg:block">
<%= live_patch("+ New Announcement",
to: Routes.announcement_new_path(@socket, :new, @current_organization),
to: Routes.announcement_new_path(@socket, :new, @current_organization.handle),
class: "border-2 rounded-md bg-white text-lg border-orange-500 py-2 px-3.5 text-sm font-medium text-orange-500 shadow-sm hover:bg-orange-500 hover:text-white"
) %>
</div>
Expand All @@ -35,12 +35,12 @@

<%= if @empty do %>
<div class="mt-32">
<.empty_state id="empty_state" url={Routes.announcement_new_path(@socket, :new, @current_organization)} placeholder="announcement" />
<.empty_state id="empty_state" url={Routes.announcement_new_path(@socket, :new, @current_organization.handle)} placeholder="announcement" />
</div>
<% else %>
<ul role="list" class="relative z-0 divide-y divide-gray-200">
<%= for announcement <- @announcements do %>
<.announcement organization={@current_organization} announcement={announcement} url={Routes.announcement_show_path(@socket, :show, @current_organization, announcement.id)} />
<.announcement organization={@current_organization} announcement={announcement} url={Routes.announcement_show_path(@socket, :show, @current_organization.handle, announcement.id)} />
<% end %>
</ul>
<% end %>
Loading