How to have a separate admin section?

I’m building my first Phoenix app and need a little guidance.

Say I build a quiz maker.

  • Admin users may create questions and answers;
  • Visitors of the website can answer the questions.

I ran the following command to create my first context:
mix phx.gen.html Quiz Question questions text:string

But what confuses me, is that I will have two clearly distinct sections of my website, with two different layouts: a layout and templates for admins, and a layout and templates for the visitors.

Most of these generated files such as the templates to list the questions, create/edit/delete a question, and so on should only be accessible by an admin user.

Also, the template to view a question show.html.eex will be different for an admin and a visitor: the visitor will have a nicely displayed question (with different html), while the admin will have a rather raw template, using the same layout as the layout used for listing questions and editing questions.

All of the admin templates will have a basic layout, while the templates for the visitors will have a layout with a nicer design.

The command above generates for exemple:
lib/quiz_maker_web/templates/question/show.html.eex
lib/quiz_maker_web/templates/question/form.html.eex
...

While I need something like:
lib/quiz_maker_web/templates/admin/question/show.html.eex
lib/quiz_maker_web/templates/admin/question/form.html.eex
lib/quiz_maker_web/templates/guest/question/show.html.eex
...

I think my requirement is a pretty basic one, but I don’t know the best way to achieve it. Any help is very much appreciated!

1 Like

:wave:

You can have “scoped” controllers like

defmodule YourAppWeb.Admin.QuestionController do
end

this would make phoenix use lib/quiz_maker_web/view/admin/question_view.ex and look in lib/quiz_maker_web/templates/admin/ for the templates.

2 Likes

You might find this useful: https://github.com/tmbb/mandarin

Hi, you can make a separate pipeline in which you specify a different template for the layout, and then use that pipeline in the scope that contains the admin routes.

For example, define a separate layout in admin.html.eex, then configure your router something like:


  pipeline :admin_pages do
    plug(:accepts, ["html"])
    plug :put_layout, {MyAppWeb.LayoutView, "admin.html"}
  end

  scope "/admin", MyAppWeb, as: :admin do
    pipe_through(:admin_pages)
    get("/", AdminController, :index)
  end

Any pages served under /admin will get the admin layout.

2 Likes

I have a login page for the admin users, managed by Admin.SessionController and displayed by new action and /templates/admin/session/new.html.eex template. I’d like this page to have the admin layout like other admin pages, and be located under /admin url.

However, the problem I have is that the /admin scope (which sets the admin layout and provides the /admin/ url part) requires authentication, so I can’t display the admin’s login page for an unauthenticated admin. How would you solve that?

scope "/admin", MyAppWebWeb, as: :admin do
  pipe_through [:browser, :authenticate_user, :admin_pages]
  resources "/questions", Admin.QuestionController
  resources "/users", Admin.UserController, except: [:edit, :update]
  resources "/sessions", Admin.SessionController, only: [:new, :create, :delete]
end

Note that I will have a SessionController for the website visitors as well, with different redirects on successful authentication, and different templates.

I just remembered about nested scope and it solved my problem about th admin login page it seems:

scope "/admin", MyAppWeb, as: :admin do
  pipe_through [:browser, :admin_pages]
  resources "/sessions", Admin.SessionController, only: [:new, :create]

  scope "/" do
    pipe_through [:authenticate_user]
    resources "/users", Admin.UserController, except: [:edit, :update]
    resources "/sessions", Admin.SessionController, only: [:delete]
  end
end

:slight_smile: