diff --git a/config/dev.exs b/config/dev.exs index 355b5f8fa..72c67d2f0 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -22,7 +22,7 @@ config :atomic, AtomicWeb.Endpoint, http: [ip: {127, 0, 0, 1}, port: 4000], check_origin: false, code_reloader: true, - debug_errors: true, + debug_errors: false, secret_key_base: "YGqNjmRjNv4pslzy4DB3Roi6xeyvS2/v/YXGn62mlMqCqjWsmu2UKbtNuNaYx5OO", watchers: [ # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args) diff --git a/lib/atomic/accounts.ex b/lib/atomic/accounts.ex index dd2877564..9d5f46563 100644 --- a/lib/atomic/accounts.ex +++ b/lib/atomic/accounts.ex @@ -295,7 +295,9 @@ defmodule Atomic.Accounts do """ def get_user_by_session_token(token) do {:ok, query} = UserToken.verify_session_token_query(token) + Repo.one(query) + |> Repo.preload(:organizations) end @doc """ @@ -453,4 +455,11 @@ defmodule Atomic.Accounts do |> Course.changeset(attrs) |> Repo.insert() end + + @doc """ + Gets the user's organizations + """ + def get_user_organizations(user) do + Repo.all(Ecto.assoc(user, :organizations)) + end end diff --git a/lib/atomic/accounts/user.ex b/lib/atomic/accounts/user.ex index b99913821..bce8add2c 100644 --- a/lib/atomic/accounts/user.ex +++ b/lib/atomic/accounts/user.ex @@ -10,7 +10,7 @@ defmodule Atomic.Accounts.User do alias Atomic.Uploaders.ProfilePicture @required_fields ~w(email password role name)a - @optional_fields ~w(course_id)a + @optional_fields ~w(course_id default_organization_id)a @roles ~w(admin student)a @@ -20,6 +20,7 @@ defmodule Atomic.Accounts.User do field :password, :string, virtual: true, redact: true field :hashed_password, :string, redact: true field :confirmed_at, :naive_datetime + belongs_to :default_organization, Organization belongs_to :course, Course field :profile_picture, ProfilePicture.Type diff --git a/lib/atomic/activities.ex b/lib/atomic/activities.ex index 69e8f9d8d..ef3b68daf 100644 --- a/lib/atomic/activities.ex +++ b/lib/atomic/activities.ex @@ -22,6 +22,15 @@ defmodule Atomic.Activities do |> Repo.all() end + def list_activities_by_organization_id(organization_id, opts) when is_list(opts) do + Activity + |> apply_filters(opts) + |> join(:inner, [a], d in assoc(a, :departments)) + |> where([a, d], d.organization_id == ^organization_id) + |> select([a, _d], a) + |> Repo.all() + end + @doc """ Gets a single activity. @@ -41,6 +50,12 @@ defmodule Atomic.Activities do |> Repo.preload(preloads) end + def get_activity_organizations!(activity, _preloads \\ []) do + departments = Map.get(activity, :departments, []) + + Enum.map(departments, & &1.organization_id) + end + alias Atomic.Activities.Enrollment def is_participating?(activity_id, user_id) do @@ -402,6 +417,19 @@ defmodule Atomic.Activities do Repo.all(Speaker) end + @doc """ + Returns the list of speakers belonging to an organization. + + ## Examples + + iex> list_speakers_by_organization_id(99d7c9e5-4212-4f59-a097-28aaa33c2621) + [%Speaker{}, ...] + + """ + def list_speakers_by_organization_id(id) do + Repo.all(from s in Speaker, where: s.organization_id == ^id) + end + def get_speakers(nil), do: [] def get_speakers(ids) do diff --git a/lib/atomic/activities/speaker.ex b/lib/atomic/activities/speaker.ex index 20e9da4b9..3dba380c4 100644 --- a/lib/atomic/activities/speaker.ex +++ b/lib/atomic/activities/speaker.ex @@ -5,18 +5,25 @@ defmodule Atomic.Activities.Speaker do use Atomic.Schema alias Atomic.Activities.Activity alias Atomic.Activities.ActivitySpeaker + alias Atomic.Organizations.Organization + + @required_fields ~w(name bio organization_id)a schema "speakers" do field :bio, :string field :name, :string + many_to_many :activities, Activity, join_through: ActivitySpeaker, on_replace: :delete + + belongs_to :organization, Organization, on_replace: :delete_if_exists + timestamps() end @doc false def changeset(speaker, attrs) do speaker - |> cast(attrs, [:name, :bio]) - |> validate_required([:name, :bio]) + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) end end diff --git a/lib/atomic/departments.ex b/lib/atomic/departments.ex index 74237c80c..024435cb4 100644 --- a/lib/atomic/departments.ex +++ b/lib/atomic/departments.ex @@ -21,6 +21,19 @@ defmodule Atomic.Departments do Repo.all(Department) end + @doc """ + Returns the list of departments belonging to an organization. + + ## Examples + + iex> list_departments_by_organization_id(99d7c9e5-4212-4f59-a097-28aaa33c2621) + [%Department{}, ...] + + """ + def list_departments_by_organization_id(id) do + Repo.all(from d in Department, where: d.organization_id == ^id) + end + def get_departments(nil), do: [] def get_departments(ids) do diff --git a/lib/atomic/organizations.ex b/lib/atomic/organizations.ex index 70eaf3a79..940f9ae84 100644 --- a/lib/atomic/organizations.ex +++ b/lib/atomic/organizations.ex @@ -3,8 +3,6 @@ defmodule Atomic.Organizations do The Organizations context. """ - import Ecto.Query, warn: false - use Atomic.Context alias Atomic.Accounts.User @@ -41,8 +39,11 @@ defmodule Atomic.Organizations do """ def get_organization!(id, preloads \\ []) do - Repo.get!(Organization, id) - |> Repo.preload(preloads) + organization = + Repo.get!(Organization, id) + |> Repo.preload(preloads) + + organization end @doc """ @@ -168,6 +169,18 @@ defmodule Atomic.Organizations do |> Repo.exists?() end + def get_role(user_id, organization_id) do + membership = + Membership + |> where([m], m.user_id == ^user_id and m.organization_id == ^organization_id) + |> Repo.one() + + case membership do + nil -> nil + _ -> membership.role + end + end + @doc """ Gets a single membership. @@ -247,12 +260,22 @@ defmodule Atomic.Organizations do """ def roles_less_than_or_equal(role) do - case role do - :follower -> [] - :member -> [:member] - :admin -> [:member, :admin] - :owner -> [:member, :admin, :owner] - end + list = [:follower, :member, :admin, :owner] + Enum.drop_while(list, fn elem -> elem != role end) + end + + @doc """ + Returns all roles bigger or equal to the given role. + + ## Examples + + iex> roles_bigger_than_or_equal(:member) + [:member, :admin, :owner] + + """ + def roles_bigger_than_or_equal(role) do + list = [:follower, :member, :admin, :owner] + Enum.drop_while(list, fn elem -> elem != role end) end @doc """ diff --git a/lib/atomic/partnerships.ex b/lib/atomic/partnerships.ex index 0f66f4738..0b9521db6 100644 --- a/lib/atomic/partnerships.ex +++ b/lib/atomic/partnerships.ex @@ -21,6 +21,19 @@ defmodule Atomic.Partnerships do Repo.all(Partner) end + @doc """ + Returns the list of partnerships belonging to an organization. + + ## Examples + + iex> list_partnerships_by_organization_id(99d7c9e5-4212-4f59-a097-28aaa33c2621) + [%Partner{}, ...] + + """ + def list_partnerships_by_organization_id(id) do + Repo.all(from p in Partner, where: p.organization_id == ^id) + end + @doc """ Gets a single partner. diff --git a/lib/atomic/partnerships/partner.ex b/lib/atomic/partnerships/partner.ex index 4c5b8be49..e907f6858 100644 --- a/lib/atomic/partnerships/partner.ex +++ b/lib/atomic/partnerships/partner.ex @@ -7,7 +7,7 @@ defmodule Atomic.Partnerships.Partner do alias Atomic.Organizations.Organization alias Atomic.Uploaders - @required_fields ~w(name description)a + @required_fields ~w(name description organization_id)a @optional_fields [] diff --git a/lib/atomic_web/config.ex b/lib/atomic_web/config.ex index f514b91f5..da7ab9824 100644 --- a/lib/atomic_web/config.ex +++ b/lib/atomic_web/config.ex @@ -4,13 +4,13 @@ defmodule AtomicWeb.Config do """ alias AtomicWeb.Router.Helpers, as: Routes - def pages(conn) do + def pages(conn, current_organization) do [ %{ - key: :activities, + key: :home, title: "Home", icon: :home, - url: Routes.activity_index_path(conn, :index), + url: Routes.home_index_path(conn, :index), tabs: [] }, %{ @@ -24,35 +24,35 @@ defmodule AtomicWeb.Config do key: :departments, title: "Departments", icon: :cube, - url: Routes.department_index_path(conn, :index, :org), + url: Routes.department_index_path(conn, :index, current_organization.id), tabs: [] }, %{ - key: :instructors, + key: :activities, title: "Activities", icon: :academic_cap, - url: Routes.activity_index_path(conn, :index), + url: Routes.activity_index_path(conn, :index, current_organization.id), tabs: [] }, %{ key: :partners, title: "Partners", icon: :user_group, - url: Routes.partner_index_path(conn, :index), + url: Routes.partner_index_path(conn, :index, current_organization.id), tabs: [] }, %{ key: :memberships, title: "Memberships", icon: :user_add, - url: Routes.membership_index_path(conn, :index, :org), + url: Routes.membership_index_path(conn, :index, current_organization.id), tabs: [] }, %{ key: :board, title: "Board", icon: :users, - url: Routes.board_index_path(conn, :index, :org), + url: Routes.board_index_path(conn, :index, current_organization.id), tabs: [] }, %{ diff --git a/lib/atomic_web/controllers/user_auth.ex b/lib/atomic_web/controllers/user_auth.ex index 134b17634..acae72d8a 100644 --- a/lib/atomic_web/controllers/user_auth.ex +++ b/lib/atomic_web/controllers/user_auth.ex @@ -6,6 +6,7 @@ defmodule AtomicWeb.UserAuth do import Phoenix.Controller alias Atomic.Accounts + alias Atomic.Organizations alias AtomicWeb.Router.Helpers, as: Routes # Make the remember me cookie valid for 60 days. @@ -31,12 +32,27 @@ defmodule AtomicWeb.UserAuth do token = Accounts.generate_user_session_token(user) user_return_to = get_session(conn, :user_return_to) - conn - |> renew_session() - |> put_session(:user_token, token) - |> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}") - |> maybe_write_remember_me_cookie(token, params) - |> redirect(to: user_return_to || signed_in_path(conn)) + case user.default_organization_id do + nil -> + conn + |> renew_session() + |> put_session(:user_token, token) + |> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}") + |> maybe_write_remember_me_cookie(token, params) + |> redirect(to: user_return_to || signed_in_path(conn)) + + _ -> + conn + |> renew_session() + |> put_session(:user_token, token) + |> put_session( + :current_organization, + Organizations.get_organization!(user.default_organization_id) + ) + |> put_session(:live_socket_id, "users_sessions:#{Base.url_encode64(token)}") + |> maybe_write_remember_me_cookie(token, params) + |> redirect(to: user_return_to || signed_in_path(conn)) + end end defp maybe_write_remember_me_cookie(conn, token, %{"remember_me" => "true"}) do @@ -94,7 +110,9 @@ defmodule AtomicWeb.UserAuth do def fetch_current_user(conn, _opts) do {user_token, conn} = ensure_user_token(conn) user = user_token && Accounts.get_user_by_session_token(user_token) - assign(conn, :current_user, user) + + conn + |> assign(:current_user, user) end defp ensure_user_token(conn) do diff --git a/lib/atomic_web/exceptions.ex b/lib/atomic_web/exceptions.ex new file mode 100644 index 000000000..f5d0f032b --- /dev/null +++ b/lib/atomic_web/exceptions.ex @@ -0,0 +1,4 @@ +defmodule AtomicWeb.MismatchError do + defexception message: "The provided parameters have no relation in the database.", + plug_status: 404 +end diff --git a/lib/atomic_web/live/activity_live/edit.ex b/lib/atomic_web/live/activity_live/edit.ex index e3b90161c..664d8ac3a 100644 --- a/lib/atomic_web/live/activity_live/edit.ex +++ b/lib/atomic_web/live/activity_live/edit.ex @@ -10,26 +10,35 @@ defmodule AtomicWeb.ActivityLive.Edit do end @impl true - def handle_params(%{"id" => id} = _params, _url, socket) do + def handle_params(%{"organization_id" => organization_id, "id" => id} = _params, _url, socket) do + activity = + Activities.get_activity!(id, preloads: [:activity_sessions, :departments, :speakers]) + entries = [ %{ name: gettext("Activities"), - route: Routes.activity_index_path(socket, :index) + route: Routes.activity_index_path(socket, :index, organization_id) }, %{ name: gettext("Edit Activity"), - route: Routes.activity_edit_path(socket, :edit, id) + route: Routes.activity_edit_path(socket, :edit, id, organization_id) } ] - {:noreply, - socket - |> assign(:breadcrumb_entries, entries) - |> assign(:current_page, :activities) - |> assign(:page_title, gettext("Edit Activity")) - |> assign( - :activity, - Activities.get_activity!(id, [:activity_sessions, :speakers, :departments]) - )} + organizations = Activities.get_activity_organizations!(activity) + + if organization_id in organizations do + {:noreply, + socket + |> assign(:breadcrumb_entries, entries) + |> assign(:current_page, :activities) + |> assign(:page_title, gettext("Edit Activity")) + |> assign( + :activity, + Activities.get_activity!(id, [:activity_sessions, :speakers, :departments]) + )} + else + raise AtomicWeb.MismatchError + end end end diff --git a/lib/atomic_web/live/activity_live/edit.html.heex b/lib/atomic_web/live/activity_live/edit.html.heex index 76dc631a2..598333bc2 100644 --- a/lib/atomic_web/live/activity_live/edit.html.heex +++ b/lib/atomic_web/live/activity_live/edit.html.heex @@ -1,3 +1,3 @@
- <.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, @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)} />
diff --git a/lib/atomic_web/live/activity_live/form_component.ex b/lib/atomic_web/live/activity_live/form_component.ex index 2c246194b..a237b7d20 100644 --- a/lib/atomic_web/live/activity_live/form_component.ex +++ b/lib/atomic_web/live/activity_live/form_component.ex @@ -7,25 +7,35 @@ defmodule AtomicWeb.ActivityLive.FormComponent do @impl true def mount(socket) do - departments = Departments.list_departments() - speakers = Activities.list_speakers() - {:ok, socket - |> assign(:departments, departments) - |> assign(:speakers, speakers)} + |> assign(:departments, []) + |> assign(:speakers, [])} end @impl true def update(%{activity: activity} = assigns, socket) do + departments = Departments.list_departments_by_organization_id(assigns.organization.id) + speakers = Activities.list_speakers_by_organization_id(assigns.organization.id) changeset = Activities.change_activity(activity) {:ok, socket |> assign(assigns) + |> assign(:departments, departments) + |> assign(:speakers, speakers) |> assign(:changeset, changeset)} end + @impl true + def update(%{"organization_id" => organization_id}, socket) do + departments = Departments.list_departments_by_organization_id(organization_id) + + {:ok, + socket + |> assign(:departments, departments)} + end + @impl true def handle_event("validate", %{"activity" => activity_params}, socket) do changeset = diff --git a/lib/atomic_web/live/activity_live/index.ex b/lib/atomic_web/live/activity_live/index.ex index 8c5bc62b0..19099c586 100644 --- a/lib/atomic_web/live/activity_live/index.ex +++ b/lib/atomic_web/live/activity_live/index.ex @@ -6,8 +6,8 @@ defmodule AtomicWeb.ActivityLive.Index do alias Atomic.Activities.Activity @impl true - def mount(_params, _session, socket) do - {:ok, assign(socket, :activities, list_activities())} + def mount(params, _session, socket) do + {:ok, assign(socket, :activities, list_activities(params["organization_id"]))} end @impl true @@ -15,7 +15,7 @@ defmodule AtomicWeb.ActivityLive.Index do entries = [ %{ name: gettext("Activities"), - route: Routes.activity_index_path(socket, :index) + route: Routes.activity_index_path(socket, :index, params["organization_id"]) } ] @@ -49,11 +49,12 @@ defmodule AtomicWeb.ActivityLive.Index do activity = Activities.get_activity!(id) {:ok, _} = Activities.delete_activity(activity) - {:noreply, assign(socket, :activies, list_activities())} + {:noreply, assign(socket, :activies, list_activities(socket.assigns.current_organization.id))} end def handle_event("open-enrollments", _payload, socket) do - {:noreply, assign(socket, :activities, list_activities())} + {:noreply, + assign(socket, :activities, list_activities(socket.assigns.current_organization.id))} end def handle_event("activities-enrolled", _payload, socket) do @@ -63,7 +64,9 @@ defmodule AtomicWeb.ActivityLive.Index do {:noreply, assign(socket, :activities, activities)} end - defp list_activities do - Activities.list_activities(preloads: [:activity_sessions, :enrollments, :speakers]) + defp list_activities(organization_id) do + Activities.list_activities_by_organization_id(organization_id, + preloads: [:activity_sessions, :enrollments, :speakers] + ) end end diff --git a/lib/atomic_web/live/activity_live/index.html.heex b/lib/atomic_web/live/activity_live/index.html.heex index d5bd032e1..eeb1f7480 100644 --- a/lib/atomic_web/live/activity_live/index.html.heex +++ b/lib/atomic_web/live/activity_live/index.html.heex @@ -21,7 +21,10 @@ @@ -33,7 +36,7 @@ -<%= live_patch("Edit", to: Routes.department_show_path(@socket, :edit, @organization, @department), class: "button") %> | -<%= live_redirect("Back", to: Routes.department_index_path(@socket, :index, @organization)) %> +<%= live_patch("Edit", to: Routes.department_show_path(@socket, :edit, @current_organization, @department), class: "button") %> | +<%= live_redirect("Back", to: Routes.department_index_path(@socket, :index, @current_organization)) %> diff --git a/lib/atomic_web/live/home_live/index.ex b/lib/atomic_web/live/home_live/index.ex new file mode 100644 index 000000000..f282e43b5 --- /dev/null +++ b/lib/atomic_web/live/home_live/index.ex @@ -0,0 +1,25 @@ +defmodule AtomicWeb.HomeLive.Index do + @moduledoc false + use AtomicWeb, :live_view + + @impl true + def mount(_params, _session, socket) do + {:ok, socket} + end + + @impl true + def handle_params(_params, _url, socket) do + entries = [ + %{ + name: gettext("Home"), + route: Routes.home_index_path(socket, :index) + } + ] + + {:noreply, + socket + |> assign(:page_title, "Home") + |> assign(:breadcrumb_entries, entries) + |> assign(:current_page, :home)} + end +end diff --git a/lib/atomic_web/live/home_live/index.html.heex b/lib/atomic_web/live/home_live/index.html.heex new file mode 100644 index 000000000..719de6cd4 --- /dev/null +++ b/lib/atomic_web/live/home_live/index.html.heex @@ -0,0 +1,3 @@ +
+

Atomic Home

+
diff --git a/lib/atomic_web/live/hooks.ex b/lib/atomic_web/live/hooks.ex index b9add6517..5a7548ec4 100644 --- a/lib/atomic_web/live/hooks.ex +++ b/lib/atomic_web/live/hooks.ex @@ -5,15 +5,74 @@ defmodule AtomicWeb.Hooks do import Phoenix.LiveView alias Atomic.Accounts + alias Atomic.Organizations def on_mount(:default, _params, _session, socket) do {:cont, assign(socket, :page_title, "Atomic")} end - def on_mount(:current_user, _params, %{"user_token" => user_token}, socket) do + def on_mount( + :authenticated_user_state, + _params, + %{"user_token" => user_token, "current_organization" => _}, + socket + ) do current_user = Accounts.get_user_by_session_token(user_token) - {:cont, assign(socket, current_user: current_user)} + if current_user.default_organization_id != nil do + socket = + socket + |> assign(:current_user, current_user) + |> assign( + :current_organization, + Organizations.get_organization!(current_user.default_organization_id) + ) + + {:cont, socket} + else + socket = + socket + |> assign(:current_user, current_user) + + {:cont, socket} + end + end + + def on_mount(:general_user_state, _params, session, socket) do + current_organization = session["current_organization"] + current_user = session["user_token"] + + case {current_organization, current_user} do + {nil, nil} -> + {:cont, socket} + + {nil, _} -> + user = Accounts.get_user_by_session_token(current_user) + + {:cont, + socket + |> assign(:current_user, user) + |> assign( + :current_organization, + Organizations.get_organization!(user.default_organization_id) + )} + + {_, nil} -> + {:cont, + socket + |> assign(:current_organization, current_organization)} + + {_, _} -> + user = Accounts.get_user_by_session_token(current_user) + + {:cont, + socket + |> assign(:current_user, user) + |> assign( + :current_organization, + Organizations.get_organization!(user.default_organization_id) + )} + end end def on_mount(:current_user, _params, _session, socket) do diff --git a/lib/atomic_web/live/membership_live/edit.ex b/lib/atomic_web/live/membership_live/edit.ex index b12c0edf0..bb418b48e 100644 --- a/lib/atomic_web/live/membership_live/edit.ex +++ b/lib/atomic_web/live/membership_live/edit.ex @@ -9,19 +9,23 @@ defmodule AtomicWeb.MembershipLive.Edit do end @impl true - def handle_params(%{"org" => org, "id" => id}, _, socket) do + def handle_params(%{"organization_id" => organization_id, "id" => id}, _, socket) do membership = Organizations.get_membership!(id, [:user, :organization, :created_by]) - {:noreply, - socket - |> assign(:page_title, page_title(socket.assigns.live_action)) - |> assign(:organization, org) - |> assign(:membership, membership) - |> assign(:current_user, socket.assigns.current_user) - |> assign( - :allowed_roles, - Organizations.roles_less_than_or_equal(socket.assigns.current_user.role) - )} + if membership.organization_id == organization_id do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:organization, organization_id) + |> assign(:membership, membership) + |> assign(:current_user, socket.assigns.current_user) + |> assign( + :allowed_roles, + Organizations.roles_less_than_or_equal(socket.assigns.current_user.role) + )} + else + raise AtomicWeb.MismatchError + end end defp page_title(:index), do: "List memberships" diff --git a/lib/atomic_web/live/membership_live/index.ex b/lib/atomic_web/live/membership_live/index.ex index 454c68208..a7d9a79b5 100644 --- a/lib/atomic_web/live/membership_live/index.ex +++ b/lib/atomic_web/live/membership_live/index.ex @@ -9,13 +9,22 @@ defmodule AtomicWeb.MembershipLive.Index do end @impl true - def handle_params(%{"org" => id}, _, socket) do + def handle_params(%{"organization_id" => id}, _, socket) do memberships = Organizations.list_memberships(%{"organization_id" => id}, [:user]) |> Enum.filter(fn m -> m.role != :follower end) + entries = [ + %{ + name: gettext("Memberships"), + route: Routes.membership_index_path(socket, :index, id) + } + ] + {:noreply, socket + |> assign(:current_page, :memberships) + |> assign(:breadcrumb_entries, entries) |> assign(:page_title, page_title(socket.assigns.live_action)) |> assign(:memberships, memberships)} end diff --git a/lib/atomic_web/live/membership_live/new.ex b/lib/atomic_web/live/membership_live/new.ex index 020fe7df3..bbe906dd5 100644 --- a/lib/atomic_web/live/membership_live/new.ex +++ b/lib/atomic_web/live/membership_live/new.ex @@ -12,12 +12,12 @@ defmodule AtomicWeb.MembershipLive.New do end @impl true - def handle_params(%{"org" => org_id}, _url, socket) do + def handle_params(%{"organization_id" => organization_id}, _url, socket) do {:noreply, socket |> assign(:page_title, gettext("New Membership")) |> assign(:membership, %Membership{ - organization_id: org_id + organization_id: organization_id }) |> assign(:users, Enum.map(Accounts.list_users(), fn u -> [key: u.email, value: u.id] end)) |> assign( diff --git a/lib/atomic_web/live/membership_live/show.ex b/lib/atomic_web/live/membership_live/show.ex index fab8a6238..9b2e1c42b 100644 --- a/lib/atomic_web/live/membership_live/show.ex +++ b/lib/atomic_web/live/membership_live/show.ex @@ -9,13 +9,17 @@ defmodule AtomicWeb.MembershipLive.Show do end @impl true - def handle_params(%{"org" => _org, "id" => id}, _, socket) do + def handle_params(%{"organization_id" => organization_id, "id" => id}, _, socket) do membership = Organizations.get_membership!(id, [:user, :organization, :created_by]) - {:noreply, - socket - |> assign(:page_title, page_title(socket.assigns.live_action)) - |> assign(:membership, membership)} + if membership.organization_id == organization_id do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:membership, membership)} + else + raise AtomicWeb.MismatchError + end end defp page_title(:index), do: "List memberships" diff --git a/lib/atomic_web/live/organization_live/index.ex b/lib/atomic_web/live/organization_live/index.ex index 8ecb9635f..09278a785 100644 --- a/lib/atomic_web/live/organization_live/index.ex +++ b/lib/atomic_web/live/organization_live/index.ex @@ -1,20 +1,43 @@ defmodule AtomicWeb.OrganizationLive.Index do use AtomicWeb, :live_view + alias Atomic.Accounts alias Atomic.Organizations alias Atomic.Organizations.Organization @impl true - def mount(_params, _session, socket) do - {:ok, assign(socket, :organizations, list_organizations())} + def mount(_params, session, socket) do + user = Accounts.get_user_by_session_token(session["user_token"]) + + {:ok, + socket + |> assign(:organizations, list_organizations()) + |> assign(:current_user, user)} end @impl true def handle_params(params, _url, socket) do - {:noreply, apply_action(socket, socket.assigns.live_action, params)} + entries = [ + %{ + name: gettext("Organizations"), + route: Routes.organization_index_path(socket, :index) + } + ] + + {:noreply, + socket + |> apply_action(socket.assigns.live_action, params) + |> assign(:breadcrumb_entries, entries) + |> assign(:current_page, :organizations)} + end + + defp apply_action(socket, :show, %{"organization_id" => id}) do + socket + |> assign(:page_title, "Show Organization") + |> assign(:organization, Organizations.get_organization!(id)) end - defp apply_action(socket, :edit, %{"id" => id}) do + defp apply_action(socket, :edit, %{"organization_id" => id}) do socket |> assign(:page_title, "Edit Organization") |> assign(:organization, Organizations.get_organization!(id)) @@ -33,7 +56,7 @@ defmodule AtomicWeb.OrganizationLive.Index do end @impl true - def handle_event("delete", %{"id" => id}, socket) do + def handle_event("delete", %{"organization_id" => id}, socket) do organization = Organizations.get_organization!(id) {:ok, _} = Organizations.delete_organization(organization) @@ -43,4 +66,8 @@ defmodule AtomicWeb.OrganizationLive.Index do defp list_organizations do Organizations.list_organizations() end + + def update_default_organization(user, organization) do + Accounts.update_user(user, %{default_organization_id: organization.id}) + end end diff --git a/lib/atomic_web/live/organization_live/index.html.heex b/lib/atomic_web/live/organization_live/index.html.heex index 7f68e2ac1..1c8bd7711 100644 --- a/lib/atomic_web/live/organization_live/index.html.heex +++ b/lib/atomic_web/live/organization_live/index.html.heex @@ -22,8 +22,8 @@ <%= organization.description %> - <%= live_redirect("Show", to: Routes.organization_show_path(@socket, :show, organization)) %> - <%= live_patch("Edit", to: Routes.organization_index_path(@socket, :edit, organization)) %> + <%= live_patch("Show", to: Routes.organization_show_path(@socket, :show, organization.id)) %> + <%= live_patch("Edit", to: Routes.organization_index_path(@socket, :edit, organization.id)) %> <%= link("Delete", to: "#", phx_click: "delete", phx_value_id: organization.id, data: [confirm: "Are you sure?"]) %> diff --git a/lib/atomic_web/live/organization_live/show.ex b/lib/atomic_web/live/organization_live/show.ex index 4b5122e24..af09ebae3 100644 --- a/lib/atomic_web/live/organization_live/show.ex +++ b/lib/atomic_web/live/organization_live/show.ex @@ -1,21 +1,42 @@ defmodule AtomicWeb.OrganizationLive.Show do use AtomicWeb, :live_view + alias Atomic.Accounts alias Atomic.Organizations @impl true - def mount(_params, _session, socket) do - {:ok, socket} + def mount(_params, session, socket) do + user = Accounts.get_user_by_session_token(session["user_token"]) + + {:ok, socket |> assign(:current_user, user)} end @impl true - def handle_params(%{"id" => id}, _, socket) do + def handle_params(%{"organization_id" => id}, _, socket) do org = Organizations.get_organization!(id, [:departments]) + user = socket.assigns.current_user + + if user.default_organization_id != id do + Accounts.update_user(user, %{"default_organization_id" => id}) + end + + entries = [ + %{ + name: gettext("Organizations"), + route: Routes.organization_index_path(socket, :index) + }, + %{ + name: gettext("Show Organization"), + route: Routes.organization_show_path(socket, :show, id) + } + ] {:noreply, socket |> assign(:page_title, page_title(socket.assigns.live_action)) |> assign(:organization, org) + |> assign(:breadcrumb_entries, entries) + |> assign(:current_page, :organizations) |> assign(:following, Organizations.is_member_of?(socket.assigns.current_user, org))} end diff --git a/lib/atomic_web/live/partner_live/form_component.ex b/lib/atomic_web/live/partner_live/form_component.ex index 64fb33643..6ee1d0d97 100644 --- a/lib/atomic_web/live/partner_live/form_component.ex +++ b/lib/atomic_web/live/partner_live/form_component.ex @@ -57,6 +57,8 @@ defmodule AtomicWeb.PartnerLive.FormComponent do end defp save_partner(socket, :new, partner_params) do + partner_params = Map.put(partner_params, "organization_id", socket.assigns.organization.id) + case Partnerships.create_partner(partner_params, &consume_image_data(socket, &1)) do {:ok, _partner} -> {:noreply, diff --git a/lib/atomic_web/live/partner_live/form_component.html.heex b/lib/atomic_web/live/partner_live/form_component.html.heex index 26d70a1d7..731c5b1fe 100644 --- a/lib/atomic_web/live/partner_live/form_component.html.heex +++ b/lib/atomic_web/live/partner_live/form_component.html.heex @@ -72,7 +72,7 @@ <%= submit do %> -
+
Save
<% end %> diff --git a/lib/atomic_web/live/partner_live/index.ex b/lib/atomic_web/live/partner_live/index.ex index 01b517731..9a4a72fd9 100644 --- a/lib/atomic_web/live/partner_live/index.ex +++ b/lib/atomic_web/live/partner_live/index.ex @@ -5,19 +5,36 @@ defmodule AtomicWeb.PartnerLive.Index do alias Atomic.Partnerships.Partner @impl true - def mount(_params, _session, socket) do - {:ok, assign(socket, :partnerships, list_partnerships())} + def mount(%{"organization_id" => organization_id}, _session, socket) do + {:ok, assign(socket, :partnerships, list_partnerships(organization_id))} end @impl true def handle_params(params, _url, socket) do - {:noreply, apply_action(socket, socket.assigns.live_action, params)} + entries = [ + %{ + name: gettext("Partnerships"), + route: Routes.partner_index_path(socket, :index, params["organization_id"]) + } + ] + + {:noreply, + socket + |> assign(:current_page, :partners) + |> assign(:breadcrumb_entries, entries) + |> apply_action(socket.assigns.live_action, params)} end - defp apply_action(socket, :edit, %{"id" => id}) do - socket - |> assign(:page_title, "Edit Partner") - |> assign(:partner, Partnerships.get_partner!(id)) + defp apply_action(socket, :edit, %{"organization_id" => organization_id, "id" => id}) do + partner = Partnerships.get_partner!(id) + + if partner.organization_id == organization_id do + socket + |> assign(:page_title, "Edit Partner") + |> assign(:partner, Partnerships.get_partner!(id)) + else + raise AtomicWeb.MismatchError + end end defp apply_action(socket, :new, _params) do @@ -37,10 +54,11 @@ defmodule AtomicWeb.PartnerLive.Index do partner = Partnerships.get_partner!(id) {:ok, _} = Partnerships.delete_partner(partner) - {:noreply, assign(socket, :partnerships, list_partnerships())} + {:noreply, + assign(socket, :partnerships, list_partnerships(socket.assigns.current_organization.id))} end - defp list_partnerships do - Partnerships.list_partnerships() + defp list_partnerships(id) do + Partnerships.list_partnerships_by_organization_id(id) end end diff --git a/lib/atomic_web/live/partner_live/index.html.heex b/lib/atomic_web/live/partner_live/index.html.heex index 7ab2cf743..7f892ded7 100644 --- a/lib/atomic_web/live/partner_live/index.html.heex +++ b/lib/atomic_web/live/partner_live/index.html.heex @@ -1,8 +1,8 @@

Listing Partnerships

<%= if @live_action in [:new, :edit] do %> - <.modal return_to={Routes.partner_index_path(@socket, :index)}> - <.live_component module={AtomicWeb.PartnerLive.FormComponent} id={@partner.id || :new} title={@page_title} action={@live_action} partner={@partner} return_to={Routes.partner_index_path(@socket, :index)} /> + <.modal return_to={Routes.partner_index_path(@socket, :index, @current_organization)}> + <.live_component module={AtomicWeb.PartnerLive.FormComponent} id={@partner.id || :new} organization={@current_organization} title={@page_title} action={@live_action} partner={@partner} return_to={Routes.partner_index_path(@socket, :index, @current_organization)} /> <% end %> @@ -22,8 +22,8 @@ <%= partner.description %> - <%= live_redirect("Show", to: Routes.partner_show_path(@socket, :show, partner)) %> - <%= live_patch("Edit", to: Routes.partner_index_path(@socket, :edit, partner)) %> + <%= live_redirect("Show", to: Routes.partner_show_path(@socket, :show, @current_organization, partner)) %> + <%= live_patch("Edit", to: Routes.partner_index_path(@socket, :edit, @current_organization, partner)) %> <%= link("Delete", to: "#", phx_click: "delete", phx_value_id: partner.id, data: [confirm: "Are you sure?"]) %> @@ -31,4 +31,4 @@ -<%= live_patch("New Partner", to: Routes.partner_index_path(@socket, :new)) %> +<%= live_patch("New Partner", to: Routes.partner_index_path(@socket, :new, @current_organization)) %> diff --git a/lib/atomic_web/live/partner_live/show.ex b/lib/atomic_web/live/partner_live/show.ex index 182ad97b5..b8a4701f9 100644 --- a/lib/atomic_web/live/partner_live/show.ex +++ b/lib/atomic_web/live/partner_live/show.ex @@ -10,11 +10,17 @@ defmodule AtomicWeb.PartnerLive.Show do end @impl true - def handle_params(%{"id" => id}, _, socket) do - {:noreply, - socket - |> assign(:page_title, page_title(socket.assigns.live_action)) - |> assign(:partner, Partnerships.get_partner!(id))} + def handle_params(%{"organization_id" => organization_id, "id" => id}, _, socket) do + partner = Partnerships.get_partner!(id) + + if partner.organization_id == organization_id do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:partner, partner)} + else + raise AtomicWeb.MismatchError + end end defp page_title(:show), do: "Show Partner" diff --git a/lib/atomic_web/live/partner_live/show.html.heex b/lib/atomic_web/live/partner_live/show.html.heex index 4f5f51268..daff0ce2e 100644 --- a/lib/atomic_web/live/partner_live/show.html.heex +++ b/lib/atomic_web/live/partner_live/show.html.heex @@ -1,8 +1,8 @@

Show Partner

<%= if @live_action in [:edit] do %> - <.modal return_to={Routes.partner_show_path(@socket, :show, @partner)}> - <.live_component module={AtomicWeb.PartnerLive.FormComponent} id={@partner.id} title={@page_title} action={@live_action} partner={@partner} return_to={Routes.partner_show_path(@socket, :show, @partner)} /> + <.modal return_to={Routes.partner_show_path(@socket, :show, @current_organization, @partner)}> + <.live_component module={AtomicWeb.PartnerLive.FormComponent} id={@partner.id} title={@page_title} action={@live_action} partner={@partner} return_to={Routes.partner_show_path(@socket, :show, @current_organization, @partner)} /> <% end %> @@ -24,5 +24,5 @@ <% end %> -<%= live_patch("Edit", to: Routes.partner_show_path(@socket, :edit, @partner), class: "button") %> | -<%= live_redirect("Back", to: Routes.partner_index_path(@socket, :index)) %> +<%= live_patch("Edit", to: Routes.partner_show_path(@socket, :edit, @current_organization, @partner), class: "button") %> | +<%= live_redirect("Back", to: Routes.partner_index_path(@socket, :index, @current_organization)) %> diff --git a/lib/atomic_web/live/scanner_live/index.ex b/lib/atomic_web/live/scanner_live/index.ex index ad2bd3e0e..2498b9e0d 100644 --- a/lib/atomic_web/live/scanner_live/index.ex +++ b/lib/atomic_web/live/scanner_live/index.ex @@ -12,10 +12,18 @@ defmodule AtomicWeb.ScannerLive.Index do @impl true def handle_params(_params, _url, socket) do + entries = [ + %{ + name: gettext("Scanner"), + route: Routes.scanner_index_path(socket, :index) + } + ] + {:noreply, socket |> assign(:current_page, :scanner) - |> assign(:title, "Scanner")} + |> assign(:title, "Scanner") + |> assign(:breadcrumb_entries, entries)} end @impl true diff --git a/lib/atomic_web/live/speaker_live/form_component.ex b/lib/atomic_web/live/speaker_live/form_component.ex index ef8bdeab0..144e7a59d 100644 --- a/lib/atomic_web/live/speaker_live/form_component.ex +++ b/lib/atomic_web/live/speaker_live/form_component.ex @@ -41,6 +41,8 @@ defmodule AtomicWeb.SpeakerLive.FormComponent do end defp save_speaker(socket, :new, speaker_params) do + speaker_params = Map.put(speaker_params, "organization_id", socket.assigns.organization.id) + case Activities.create_speaker(speaker_params) do {:ok, _speaker} -> {:noreply, diff --git a/lib/atomic_web/live/speaker_live/index.ex b/lib/atomic_web/live/speaker_live/index.ex index 85bc39238..e0956eb58 100644 --- a/lib/atomic_web/live/speaker_live/index.ex +++ b/lib/atomic_web/live/speaker_live/index.ex @@ -5,8 +5,8 @@ defmodule AtomicWeb.SpeakerLive.Index do alias Atomic.Activities.Speaker @impl true - def mount(_params, _session, socket) do - {:ok, assign(socket, :speakers, list_speakers())} + def mount(%{"organization_id" => organization_id}, _session, socket) do + {:ok, assign(socket, :speakers, list_speakers(organization_id))} end @impl true @@ -14,7 +14,7 @@ defmodule AtomicWeb.SpeakerLive.Index do entries = [ %{ name: gettext("Speakers"), - route: Routes.speaker_index_path(socket, :index) + route: Routes.speaker_index_path(socket, :index, params["organization_id"]) } ] @@ -25,10 +25,16 @@ defmodule AtomicWeb.SpeakerLive.Index do |> apply_action(socket.assigns.live_action, params)} end - defp apply_action(socket, :edit, %{"id" => id}) do - socket - |> assign(:page_title, "Edit Speaker") - |> assign(:speaker, Activities.get_speaker!(id)) + defp apply_action(socket, :edit, %{"organization_id" => organization_id, "id" => id}) do + speaker = Activities.get_speaker!(id) + + if speaker.organization_id == organization_id do + socket + |> assign(:page_title, "Edit Speaker") + |> assign(:speaker, speaker) + else + raise AtomicWeb.MismatchError + end end defp apply_action(socket, :new, _params) do @@ -48,10 +54,10 @@ defmodule AtomicWeb.SpeakerLive.Index do speaker = Activities.get_speaker!(id) {:ok, _} = Activities.delete_speaker(speaker) - {:noreply, assign(socket, :speakers, list_speakers())} + {:noreply, assign(socket, :speakers, list_speakers(socket.assigns.current_organization.id))} end - defp list_speakers do - Activities.list_speakers() + defp list_speakers(organization_id) do + Activities.list_speakers_by_organization_id(organization_id) end end diff --git a/lib/atomic_web/live/speaker_live/index.html.heex b/lib/atomic_web/live/speaker_live/index.html.heex index 17ffbc9c9..ed87b8f1b 100644 --- a/lib/atomic_web/live/speaker_live/index.html.heex +++ b/lib/atomic_web/live/speaker_live/index.html.heex @@ -1,8 +1,8 @@

Listing Speakers

<%= if @live_action in [:new, :edit] do %> - <.modal return_to={Routes.speaker_index_path(@socket, :index)}> - <.live_component module={AtomicWeb.SpeakerLive.FormComponent} id={@speaker.id || :new} title={@page_title} action={@live_action} speaker={@speaker} return_to={Routes.speaker_index_path(@socket, :index)} /> + <.modal return_to={Routes.speaker_index_path(@socket, :index, @current_organization)}> + <.live_component module={AtomicWeb.SpeakerLive.FormComponent} id={@speaker.id || :new} organization={@current_organization} title={@page_title} action={@live_action} speaker={@speaker} return_to={Routes.speaker_index_path(@socket, :index, @current_organization)} /> <% end %> @@ -22,8 +22,8 @@ <%= speaker.bio %> - <%= live_redirect("Show", to: Routes.speaker_show_path(@socket, :show, speaker)) %> - <%= live_patch("Edit", to: Routes.speaker_index_path(@socket, :edit, speaker)) %> + <%= live_redirect("Show", to: Routes.speaker_show_path(@socket, :show, @current_organization, speaker)) %> + <%= live_patch("Edit", to: Routes.speaker_index_path(@socket, :edit, @current_organization, speaker)) %> <%= link("Delete", to: "#", phx_click: "delete", phx_value_id: speaker.id, data: [confirm: "Are you sure?"]) %> @@ -31,4 +31,4 @@ -<%= live_patch("New Speaker", to: Routes.speaker_index_path(@socket, :new)) %> +<%= live_patch("New Speaker", to: Routes.speaker_index_path(@socket, :new, @current_organization)) %> diff --git a/lib/atomic_web/live/speaker_live/show.ex b/lib/atomic_web/live/speaker_live/show.ex index eb5b4b47d..49a3818b4 100644 --- a/lib/atomic_web/live/speaker_live/show.ex +++ b/lib/atomic_web/live/speaker_live/show.ex @@ -9,24 +9,30 @@ defmodule AtomicWeb.SpeakerLive.Show do end @impl true - def handle_params(%{"id" => id}, _, socket) do + def handle_params(%{"organization_id" => organization_id, "id" => id}, _, socket) do + speaker = Activities.get_speaker!(id) + entries = [ %{ name: gettext("Speakers"), - route: Routes.speaker_index_path(socket, :index) + route: Routes.speaker_index_path(socket, :index, organization_id) }, %{ name: gettext("Show"), - route: Routes.speaker_show_path(socket, :show, id) + route: Routes.speaker_show_path(socket, :show, organization_id, id) } ] - {:noreply, - socket - |> assign(:page_title, page_title(socket.assigns.live_action)) - |> assign(:current_page, :speakers) - |> assign(:breadcrumb_entries, entries) - |> assign(:speaker, Activities.get_speaker!(id))} + if speaker.organization_id == organization_id do + {:noreply, + socket + |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:current_page, :speakers) + |> assign(:breadcrumb_entries, entries) + |> assign(:speaker, Activities.get_speaker!(id))} + else + raise AtomicWeb.MismatchError + end end defp page_title(:show), do: "Show Speaker" diff --git a/lib/atomic_web/live/speaker_live/show.html.heex b/lib/atomic_web/live/speaker_live/show.html.heex index 73999d02c..b108acb76 100644 --- a/lib/atomic_web/live/speaker_live/show.html.heex +++ b/lib/atomic_web/live/speaker_live/show.html.heex @@ -1,8 +1,8 @@

Show Speaker

<%= if @live_action in [:edit] do %> - <.modal return_to={Routes.speaker_show_path(@socket, :show, @speaker)}> - <.live_component module={AtomicWeb.SpeakerLive.FormComponent} id={@speaker.id} title={@page_title} action={@live_action} speaker={@speaker} return_to={Routes.speaker_show_path(@socket, :show, @speaker)} /> + <.modal return_to={Routes.speaker_show_path(@socket, :show, @current_organization, @speaker)}> + <.live_component module={AtomicWeb.SpeakerLive.FormComponent} id={@speaker.id} organization={@current_organization} title={@page_title} action={@live_action} speaker={@speaker} return_to={Routes.speaker_show_path(@socket, :show, @current_organization, @speaker)} /> <% end %> @@ -18,5 +18,5 @@ -<%= live_patch("Edit", to: Routes.speaker_show_path(@socket, :edit, @speaker), class: "button") %> | -<%= live_redirect("Back", to: Routes.speaker_index_path(@socket, :index)) %> +<%= live_patch("Edit", to: Routes.speaker_show_path(@socket, :edit, @current_organization, @speaker), class: "button") %> | +<%= live_redirect("Back", to: Routes.speaker_index_path(@socket, :index, @current_organization)) %> diff --git a/lib/atomic_web/live/user_live/edit.html.heex b/lib/atomic_web/live/user_live/edit.html.heex index dffb93cee..4896ca8b5 100644 --- a/lib/atomic_web/live/user_live/edit.html.heex +++ b/lib/atomic_web/live/user_live/edit.html.heex @@ -1 +1 @@ -<.live_component module={AtomicWeb.UserLive.FormComponent} user={@user} id={@current_user.id} courses={@courses} title={@page_title} action={@live_action} return_to={Routes.activity_index_path(@socket, :index)} /> +<.live_component module={AtomicWeb.UserLive.FormComponent} user={@user} id={@current_user.id} courses={@courses} title={@page_title} action={@live_action} return_to={Routes.home_index_path(@conn, :index)} /> diff --git a/lib/atomic_web/plugs/authorize.ex b/lib/atomic_web/plugs/authorize.ex new file mode 100644 index 000000000..62492fa4c --- /dev/null +++ b/lib/atomic_web/plugs/authorize.ex @@ -0,0 +1,48 @@ +defmodule AtomicWeb.Plugs.Authorize do + @moduledoc false + import Plug.Conn + + alias Atomic.Organizations + + def init(opts), do: opts + + def call(conn, minimum_authorized_role) do + if authorized?(conn, minimum_authorized_role) do + conn + else + conn + |> send_resp(:not_found, "") + |> halt() + end + end + + defp authorized?(conn, minimum_authorized_role) do + organization_id = get_organization_id(conn) + + case {organization_id, conn.assigns.current_user} do + {nil, _} -> + false + + {id, user} -> + user_authorized?(user, id, minimum_authorized_role) + end + end + + defp get_organization_id(conn) do + case conn.params["organization_id"] do + organization_id when is_binary(organization_id) -> + organization_id + + _ -> + nil + end + end + + defp user_authorized?(user, organization_id, minimum_authorized_role) do + user_organizations = Enum.map(user.organizations, & &1.id) + role = Organizations.get_role(user.id, organization_id) + allowed_roles = Organizations.roles_bigger_than_or_equal(minimum_authorized_role) + + organization_id in user_organizations && role in allowed_roles + end +end diff --git a/lib/atomic_web/router.ex b/lib/atomic_web/router.ex index 04f31502e..9de88ed12 100644 --- a/lib/atomic_web/router.ex +++ b/lib/atomic_web/router.ex @@ -17,62 +17,90 @@ defmodule AtomicWeb.Router do plug :accepts, ["json"] end + pipeline :admin do + plug AtomicWeb.Plugs.Authorize, :admin + end + + pipeline :member do + plug AtomicWeb.Plugs.Authorize, :member + end + + pipeline :follower do + plug AtomicWeb.Plugs.Authorize, :follower + end + scope "/", AtomicWeb do pipe_through :browser + + live_session :general, on_mount: [{AtomicWeb.Hooks, :general_user_state}] do + live "/", HomeLive.Index, :index + + live "/organizations", OrganizationLive.Index, :index + live "/organizations/:organization_id", OrganizationLive.Show, :show + + scope "/organizations/:organization_id" do + live "/board/", BoardLive.Index, :index + live "/board/:id", BoardLive.Show, :show + end + end end scope "/", AtomicWeb do pipe_through [:browser, :require_authenticated_user] - live_session :logged_in, on_mount: [{AtomicWeb.Hooks, :current_user}] do - live "/", ActivityLive.Index, :index + live_session :logged_in, on_mount: [{AtomicWeb.Hooks, :authenticated_user_state}] do live "/scanner", ScannerLive.Index, :index - live "/activities", ActivityLive.Index, :index - live "/activities/new", ActivityLive.New, :new - live "/activities/:id/edit", ActivityLive.Edit, :edit - live "/activities/:id", ActivityLive.Show, :show - - live "/departments/:org", DepartmentLive.Index, :index - live "/departments/:org/new", DepartmentLive.Index, :new - live "/departments/:org/:id/edit", DepartmentLive.Index, :edit - live "/departments/:org/:id", DepartmentLive.Show, :show - live "/departments/:org/:id/show/edit", DepartmentLive.Show, :edit - - live "/partners", PartnerLive.Index, :index - live "/partners/new", PartnerLive.Index, :new - live "/partners/:id/edit", PartnerLive.Index, :edit - live "/partners/:id", PartnerLive.Show, :show - live "/partners/:id/show/edit", PartnerLive.Show, :edit - - live "/speakers", SpeakerLive.Index, :index - live "/speakers/new", SpeakerLive.Index, :new - live "/speakers/:id/edit", SpeakerLive.Index, :edit - live "/speakers/:id", SpeakerLive.Show, :show - live "/speakers/:id/show/edit", SpeakerLive.Show, :edit - live "/organizations", OrganizationLive.Index, :index - live "/organizations/new", OrganizationLive.Index, :new - live "/organizations/:id/edit", OrganizationLive.Index, :edit - live "/organizations/:id", OrganizationLive.Show, :show - live "/organizations/:id/show/edit", OrganizationLive.Show, :edit + scope "/organizations/:organization_id" do + pipe_through :admin + live "/edit", OrganizationLive.Index, :edit + live "/show/edit", OrganizationLive.Show, :edit - live "/membership/:org", MembershipLive.Index, :index - live "/membership/:org/new", MembershipLive.New, :new - live "/membership/:org/:id", MembershipLive.Show, :show - live "/membership/:org/:id/edit", MembershipLive.Edit, :edit + live "/activities/new", ActivityLive.New, :new + live "/activities/:id/edit", ActivityLive.Edit, :edit - live "/card/:membership_id", CardLive.Show, :show + live "/departments/new", DepartmentLive.Index, :new + live "/departments/:id/edit", DepartmentLive.Index, :edit + live "/departments/:id/show/edit", DepartmentLive.Show, :edit + + live "/partners/new", PartnerLive.Index, :new + live "/partners/:id/edit", PartnerLive.Index, :edit + live "/partners/:id/show/edit", PartnerLive.Show, :edit + + live "/speakers/new", SpeakerLive.Index, :new + live "/speakers/:id/edit", SpeakerLive.Index, :edit + live "/speakers/:id/show/edit", SpeakerLive.Show, :edit + + live "/board/new", BoardLive.New, :new + live "/board/:id/edit", BoardLive.Edit, :edit + + live "/memberships", MembershipLive.Index, :index + live "/memberships/new", MembershipLive.New, :new + live "/memberships/:id", MembershipLive.Show, :show + live "/memberships/:id/edit", MembershipLive.Edit, :edit + end + + scope "/organizations/:organization_id" do + pipe_through :follower + live "/activities", ActivityLive.Index, :index + live "/activities/:id", ActivityLive.Show, :show - live "/board/:org", BoardLive.Index, :index - live "/board/:org/new", BoardLive.New, :new - live "/board/:org/:id", BoardLive.Show, :show - live "/board/:org/:id/edit", BoardLive.Edit, :edit - live "/memberships/:org", MembershipLive.Index, :index - live "/memberships/:org/new", MembershipLive.New, :new - live "/memberships/:org/:id", MembershipLive.Show, :show - live "/memberships/:org/:id/edit", MembershipLive.Edit, :edit + live "/departments", DepartmentLive.Index, :index + live "/departments/:id", DepartmentLive.Show, :show + + live "/partners", PartnerLive.Index, :index + live "/partners/:id", PartnerLive.Show, :show + + live "/speakers", SpeakerLive.Index, :index + live "/speakers/:id", SpeakerLive.Show, :show + end + + live "/organizations/new", OrganizationLive.Index, :new live "/user/edit", UserLive.Edit, :edit + + pipe_through :member + live "/card/:membership_id", CardLive.Show, :show end end diff --git a/lib/atomic_web/templates/error/404.html.heex b/lib/atomic_web/templates/error/404.html.heex new file mode 100644 index 000000000..3678ddf7d --- /dev/null +++ b/lib/atomic_web/templates/error/404.html.heex @@ -0,0 +1,37 @@ + + + + + + + + <%= live_title_tag(assigns[:page_title] || "Store", suffix: " · 404") %> + + + + + + +
+
+
+
+

404

+
+

Page Not Found

+
+
+

+ We can't seem to find the page you're looking for.
The link you followed may be broken, or the page may have been removed. +

+ +
+
+
+ diff --git a/lib/atomic_web/templates/layout/live.html.heex b/lib/atomic_web/templates/layout/live.html.heex index e810c6c0e..8b6bf1b49 100644 --- a/lib/atomic_web/templates/layout/live.html.heex +++ b/lib/atomic_web/templates/layout/live.html.heex @@ -18,15 +18,15 @@ - <%= Atomic.Accounts.extract_initials("CeSIUM") %> + <%= Atomic.Accounts.extract_initials(@current_organization.name) %> - CeSIUM + <%= @current_organization.name %> - Role + <%= AtomicWeb.ViewUtils.capitalize_first_letter(Atomic.Organizations.get_role(@current_user.id, @current_organization.id)) %> @@ -54,34 +54,36 @@ aria-labelledby="options-menu-button" tabindex="-1" > -
- - - - - - <%= Atomic.Accounts.extract_initials("NECC") %> + <%= for organization <- Atomic.Accounts.get_user_organizations(@current_user) do %> +
+ + + + + + <%= Atomic.Accounts.extract_initials(organization.name) %> + - - - - - <%= "NECC" %> - - - <%= "Role" %> + + + + <%= organization.name %> + + + <%= AtomicWeb.ViewUtils.capitalize_first_letter(Atomic.Organizations.get_role(@current_user.id, organization.id)) %> + - - -
+ +
+ <% end %>