Merge branch 'page-templates' into 'master'

feat: Add good basic page templates

See merge request mythic-insight/legendary!13
This commit is contained in:
Robert Prehn 2020-07-30 16:05:09 +00:00
commit a01763919d
61 changed files with 292 additions and 306 deletions

View file

@ -7,6 +7,7 @@ defmodule Admin.Router do
plug :fetch_flash plug :fetch_flash
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end end
pipeline :require_admin do pipeline :require_admin do

View file

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title><%= I18n.t! "en", "site.title" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
<header>
<section class="container">
<nav role="navigation">
<ul>
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
<%= if function_exported?(Routes, :live_dashboard_path, 2) do %>
<li><%= link "LiveDashboard", to: Routes.live_dashboard_path(@conn, :home) %></li>
<% end %>
</ul>
</nav>
<a href="https://phoenixframework.org/" class="phx-logo">
<img src="<%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
</a>
</section>
</header>
<main role="main" class="container">
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= @inner_content %>
</main>
</body>
</html>

View file

@ -9,23 +9,23 @@ defmodule App.Application do
children = [ children = [
App.Repo, App.Repo,
# Start the Telemetry supervisor # Start the Telemetry supervisor
App.Telemetry, AppWeb.Telemetry,
# Start the Endpoint (http/https) # Start the Endpoint (http/https)
App.Endpoint AppWeb.Endpoint
# Start a worker by calling: App.Worker.start_link(arg) # Start a worker by calling: AppWeb.Worker.start_link(arg)
# {App.Worker, arg} # {AppWeb.Worker, arg}
] ]
# See https://hexdocs.pm/elixir/Supervisor.html # See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options # for other strategies and supported options
opts = [strategy: :one_for_one, name: App.Supervisor] opts = [strategy: :one_for_one, name: AppWeb.Supervisor]
Supervisor.start_link(children, opts) Supervisor.start_link(children, opts)
end end
# Tell Phoenix to update the endpoint configuration # Tell Phoenix to update the endpoint configuration
# whenever the application is updated. # whenever the application is updated.
def config_change(changed, _new, removed) do def config_change(changed, _new, removed) do
App.Endpoint.config_change(changed, removed) AppWeb.Endpoint.config_change(changed, removed)
:ok :ok
end end
end end

View file

@ -1,7 +0,0 @@
defmodule App.PageController do
use App, :controller
def index(conn, _params) do
render(conn, "index.html")
end
end

View file

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>App · Phoenix Framework</title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
<header>
<section class="container">
<nav role="navigation">
<ul>
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
<%= if function_exported?(Routes, :live_dashboard_path, 2) do %>
<li><%= link "LiveDashboard", to: Routes.live_dashboard_path(@conn, :home) %></li>
<% end %>
</ul>
</nav>
<a href="https://phoenixframework.org/" class="phx-logo">
<img src="<%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
</a>
</section>
</header>
<main role="main" class="container">
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= @inner_content %>
</main>
</body>
</html>

View file

@ -1,3 +0,0 @@
defmodule App.LayoutView do
use App, :view
end

View file

@ -1,3 +0,0 @@
defmodule App.PageView do
use App, :view
end

View file

@ -1,12 +1,12 @@
defmodule App do defmodule AppWeb do
@moduledoc """ @moduledoc """
The entrypoint for defining your web interface, such The entrypoint for defining your web interface, such
as controllers, views, channels and so on. as controllers, views, channels and so on.
This can be used in your application as: This can be used in your application as:
use App, :controller use AppWeb, :controller
use App, :view use AppWeb, :view
The definitions below will be executed for every view, The definitions below will be executed for every view,
controller, etc, so keep them short and clean, focused controller, etc, so keep them short and clean, focused
@ -19,19 +19,19 @@ defmodule App do
def controller do def controller do
quote do quote do
use Phoenix.Controller, namespace: App use Phoenix.Controller, namespace: AppWeb
import Plug.Conn import Plug.Conn
import App.Gettext import AppWeb.Gettext
alias App.Router.Helpers, as: Routes alias AppWeb.Router.Helpers, as: Routes
end end
end end
def view do def view do
quote do quote do
use Phoenix.View, use Phoenix.View,
root: "lib/app/templates", root: "lib/app_web/templates",
namespace: App namespace: AppWeb
# Import convenience functions from controllers # Import convenience functions from controllers
import Phoenix.Controller, import Phoenix.Controller,
@ -54,7 +54,7 @@ defmodule App do
def channel do def channel do
quote do quote do
use Phoenix.Channel use Phoenix.Channel
import App.Gettext import AppWeb.Gettext
end end
end end
@ -66,9 +66,10 @@ defmodule App do
# Import basic rendering functionality (render, render_layout, etc) # Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View import Phoenix.View
import App.ErrorHelpers import CoreWeb.Helpers
import App.Gettext import AppWeb.ErrorHelpers
alias App.Router.Helpers, as: Routes import AppWeb.Gettext
alias AppWeb.Router.Helpers, as: Routes
end end
end end

View file

@ -1,8 +1,8 @@
defmodule App.UserSocket do defmodule AppWeb.UserSocket do
use Phoenix.Socket use Phoenix.Socket
## Channels ## Channels
# channel "room:*", App.RoomChannel # channel "room:*", AppWeb.RoomChannel
# Socket params are passed from the client and can # Socket params are passed from the client and can
# be used to verify and authenticate a user. After # be used to verify and authenticate a user. After
@ -27,7 +27,7 @@ defmodule App.UserSocket do
# Would allow you to broadcast a "disconnect" event and terminate # Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user: # all active sockets and channels for a given user:
# #
# App.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{}) # AppWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
# #
# Returning `nil` makes this socket anonymous. # Returning `nil` makes this socket anonymous.
@impl true @impl true

View file

View file

@ -1,4 +1,4 @@
defmodule App.Endpoint do defmodule AppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :app use Phoenix.Endpoint, otp_app: :app
# The session will be stored in the cookie and signed, # The session will be stored in the cookie and signed,
@ -10,7 +10,7 @@ defmodule App.Endpoint do
signing_salt: "VQMRz57g" signing_salt: "VQMRz57g"
] ]
socket "/socket", App.UserSocket, socket "/socket", AppWeb.UserSocket,
websocket: true, websocket: true,
longpoll: false longpoll: false
@ -50,5 +50,5 @@ defmodule App.Endpoint do
plug Plug.MethodOverride plug Plug.MethodOverride
plug Plug.Head plug Plug.Head
plug Plug.Session, @session_options plug Plug.Session, @session_options
plug App.Router plug AppWeb.Router
end end

View file

@ -1,11 +1,11 @@
defmodule App.Gettext do defmodule AppWeb.Gettext do
@moduledoc """ @moduledoc """
A module providing Internationalization with a gettext-based API. A module providing Internationalization with a gettext-based API.
By using [Gettext](https://hexdocs.pm/gettext), By using [Gettext](https://hexdocs.pm/gettext),
your module gains a set of macros for translations, for example: your module gains a set of macros for translations, for example:
import App.Gettext import AppWeb.Gettext
# Simple translation # Simple translation
gettext("Here is the string to translate") gettext("Here is the string to translate")

View file

@ -1,5 +1,5 @@
defmodule App.Router do defmodule AppWeb.Router do
use App, :router use AppWeb, :router
pipeline :browser do pipeline :browser do
plug :accepts, ["html"] plug :accepts, ["html"]
@ -7,20 +7,21 @@ defmodule App.Router do
plug :fetch_flash plug :fetch_flash
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end end
pipeline :api do pipeline :api do
plug :accepts, ["json"] plug :accepts, ["json"]
end end
scope "/", App do scope "/", AppWeb do
pipe_through :browser pipe_through :browser
get "/", PageController, :index get "/", PageController, :index
end end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.
# scope "/api", App do # scope "/api", AppWeb do
# pipe_through :api # pipe_through :api
# end # end
@ -36,7 +37,7 @@ defmodule App.Router do
scope "/" do scope "/" do
pipe_through :browser pipe_through :browser
live_dashboard "/dashboard", metrics: App.Telemetry live_dashboard "/dashboard", metrics: AppWeb.Telemetry
end end
end end
end end

View file

@ -1,4 +1,4 @@
defmodule App.Telemetry do defmodule AppWeb.Telemetry do
use Supervisor use Supervisor
import Telemetry.Metrics import Telemetry.Metrics
@ -49,7 +49,7 @@ defmodule App.Telemetry do
[ [
# A module, function and arguments to be invoked periodically. # A module, function and arguments to be invoked periodically.
# This function must call :telemetry.execute/3 and a metric must be added above. # This function must call :telemetry.execute/3 and a metric must be added above.
# {App, :count_users, []} # {AppWeb, :count_users, []}
] ]
end end
end end

View file

@ -1,4 +1,4 @@
defmodule App.ErrorHelpers do defmodule AppWeb.ErrorHelpers do
@moduledoc """ @moduledoc """
Conveniences for translating and building error messages. Conveniences for translating and building error messages.
""" """
@ -39,9 +39,9 @@ defmodule App.ErrorHelpers do
# should be written to the errors.po file. The :count option is # should be written to the errors.po file. The :count option is
# set by Ecto and indicates we should also apply plural rules. # set by Ecto and indicates we should also apply plural rules.
if count = opts[:count] do if count = opts[:count] do
Gettext.dngettext(App.Gettext, "errors", msg, msg, count, opts) Gettext.dngettext(AppWeb.Gettext, "errors", msg, msg, count, opts)
else else
Gettext.dgettext(App.Gettext, "errors", msg, opts) Gettext.dgettext(AppWeb.Gettext, "errors", msg, opts)
end end
end end
end end

View file

@ -1,5 +1,5 @@
defmodule App.ErrorView do defmodule AppWeb.ErrorView do
use App, :view use AppWeb, :view
# If you want to customize a particular status code # If you want to customize a particular status code
# for a certain format, you may uncomment below. # for a certain format, you may uncomment below.

View file

@ -0,0 +1,3 @@
defmodule AppWeb.LayoutView do
use AppWeb, :view
end

View file

@ -0,0 +1,3 @@
defmodule AppWeb.PageView do
use AppWeb, :view
end

View file

@ -37,6 +37,7 @@ defmodule App.MixProject do
# Type `mix help deps` for examples and options. # Type `mix help deps` for examples and options.
defp deps do defp deps do
[ [
{:core, in_umbrella: true},
{:ecto_sql, "~> 3.4"}, {:ecto_sql, "~> 3.4"},
{:phoenix, "~> 1.5.3"}, {:phoenix, "~> 1.5.3"},
{:phoenix_ecto, "~> 4.0"}, {:phoenix_ecto, "~> 4.0"},

View file

@ -1,5 +1,19 @@
<h1>Edit <%= schema.human_singular %></h1> <div class="ui top padded container">
<div class="ui grid">
<div class="row">
<div class="eight wide column">
<h1 class="ui header">Edit <%= schema.human_singular %></h1>
</div>
<div class="eight wide right aligned column">
<span><%%= link "Back", to: Routes.<%= schema.route_helper %>_path(@conn, :index), class: "ui button" %></span>
</div>
</div>
<%%= render "form.html", Map.put(assigns, :action, Routes.<%= schema.route_helper %>_path(@conn, :update, @<%= schema.singular %>)) %> <div class="centered row">
<div class="center aligned column">
<span><%%= link "Back", to: Routes.<%= schema.route_helper %>_path(@conn, :index) %></span> <%%= changeset_error_block(@changeset) %>
<%%= render "form.html", Map.put(assigns, :action, Routes.<%= schema.route_helper %>_path(@conn, :update, @<%= schema.singular %>)) %>
</div>
</div>
</div>
</div>

View file

@ -1,15 +1,10 @@
<%%= form_for @changeset, @action, fn f -> %> <%%= form_for @changeset, @action, [class: "ui large form"], fn f -> %>
<%%= if @changeset.action do %> <div class="ui stacked left aligned segment">
<div class="alert alert-danger"> <%= for input <- Mix.Legendary.inputs(schema) do %>
<p>Oops, something went wrong! Please check the errors below.</p> <%= input %>
</div>
<%% end %>
<%= for {label, input, error} <- inputs, input do %>
<%= label %>
<%= input %>
<%= error %>
<% end %> <% end %>
<div> <div>
<%%= submit "Save" %> <%%= submit "Save", class: "ui primary fluid large submit button" %>
</div>
</div> </div>
<%% end %> <%% end %>

View file

@ -1,26 +1,46 @@
<h1>Listing <%= schema.human_plural %></h1> <div class="ui top padded container">
<div class="ui grid">
<div class="row">
<div class="eight wide column">
<h1 class="ui header"><%= schema.human_plural %></h1>
</div>
<div class="eight wide right aligned column">
<span><%%= link "New <%= schema.human_singular %>", to: Routes.<%= schema.route_helper %>_path(@conn, :new), class: "ui primary button" %></span>
</div>
</div>
</div>
<table> <table class="ui celled table">
<thead> <thead>
<tr> <tr>
<%= for {k, _} <- schema.attrs do %> <th><%= Phoenix.Naming.humanize(Atom.to_string(k)) %></th> <%= for {k, _} <- schema.attrs do %> <th><%= Phoenix.Naming.humanize(Atom.to_string(k)) %></th>
<% end %> <% end %>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<%%= for <%= schema.singular %> <- @<%= schema.plural %> do %> <%%= case @<%= schema.plural %> do %>
<tr> <%%= [] -> %>
<%= for {k, _} <- schema.attrs do %> <td><%%= <%= schema.singular %>.<%= k %> %></td> <tr>
<% end %> <td colspan="<%= schema.attrs |> Enum.count() %>">
<td> No results.
<span><%%= link "Show", to: Routes.<%= schema.route_helper %>_path(@conn, :show, <%= schema.singular %>) %></span> </td>
<span><%%= link "Edit", to: Routes.<%= schema.route_helper %>_path(@conn, :edit, <%= schema.singular %>) %></span> </tr>
<span><%%= link "Delete", to: Routes.<%= schema.route_helper %>_path(@conn, :delete, <%= schema.singular %>), method: :delete, data: [confirm: "Are you sure?"] %></span> <%%= _ -> %>
</td> <%%= for <%= schema.singular %> <- @<%= schema.plural %> do %>
</tr> <tr>
<%% end %> <%= for {k, _} <- schema.attrs do %> <td><%%= <%= schema.singular %>.<%= k %> %></td>
</tbody> <% end %>
</table> <td>
<div class="ui list">
<span><%%= link "New <%= schema.human_singular %>", to: Routes.<%= schema.route_helper %>_path(@conn, :new) %></span> <span class="item"><%%= link "Show", to: Routes.<%= schema.route_helper %>_path(@conn, :show, <%= schema.singular %>) %></span>
<span class="item"><%%= link "Edit", to: Routes.<%= schema.route_helper %>_path(@conn, :edit, <%= schema.singular %>) %></span>
<span class="item"><%%= link "Delete", to: Routes.<%= schema.route_helper %>_path(@conn, :delete, <%= schema.singular %>), method: :delete, data: [confirm: "Are you sure?"] %></span>
</div>
</td>
</tr>
<%% end %>
<%% end %>
</tbody>
</table>
</div>

View file

@ -1,5 +1,19 @@
<h1>New <%= schema.human_singular %></h1> <div class="ui top padded container">
<div class="ui grid">
<div class="row">
<div class="eight wide column">
<h1 class="ui header">New <%= schema.human_singular %></h1>
</div>
<div class="eight wide right aligned column">
<span><%%= link "Back", to: Routes.<%= schema.route_helper %>_path(@conn, :index), class: "ui button" %></span>
</div>
</div>
<%%= render "form.html", Map.put(assigns, :action, Routes.<%= schema.route_helper %>_path(@conn, :create)) %> <div class="centered row">
<div class="center aligned column">
<span><%%= link "Back", to: Routes.<%= schema.route_helper %>_path(@conn, :index) %></span> <%%= changeset_error_block(@changeset) %>
<%%= render "form.html", Map.put(assigns, :action, Routes.<%= schema.route_helper %>_path(@conn, :create)) %>
</div>
</div>
</div>
</div>

View file

@ -1,13 +1,26 @@
<h1>Show <%= schema.human_singular %></h1> <div class="ui top padded container">
<div class="ui grid">
<div class="row">
<div class="eight wide column">
<h1 class="ui header"><%= schema.human_singular %></h1>
</div>
<div class="eight wide right aligned column">
<%%= link "Edit", to: Routes.<%= schema.route_helper %>_path(@conn, :edit, @<%= schema.singular %>), class: "ui button primary" %>
<span><%%= link "Back", to: Routes.<%= schema.route_helper %>_path(@conn, :index), class: "ui button" %></span>
</div>
</div>
</div>
<ul> <div class="ui segment">
<%= for {k, _} <- schema.attrs do %> <div class="ui list">
<li> <%= for {k, _} <- schema.attrs do %>
<strong><%= Phoenix.Naming.humanize(Atom.to_string(k)) %>:</strong> <div class="item">
<%%= @<%= schema.singular %>.<%= k %> %> <div class="content">
</li> <strong><%= Phoenix.Naming.humanize(Atom.to_string(k)) %>:</strong>
<% end %> <%%= @<%= schema.singular %>.<%= k %> %>
</ul> </div>
</div>
<span><%%= link "Edit", to: Routes.<%= schema.route_helper %>_path(@conn, :edit, @<%= schema.singular %>) %></span> <% end %>
<span><%%= link "Back", to: Routes.<%= schema.route_helper %>_path(@conn, :index) %></span> </div>
</div>
</div>

View file

View file

@ -1,8 +0,0 @@
defmodule App.PageControllerTest do
use App.ConnCase
test "GET /", %{conn: conn} do
conn = get(conn, "/")
assert html_response(conn, 200) =~ "Welcome to Phoenix!"
end
end

View file

@ -1,14 +1,14 @@
defmodule App.ErrorViewTest do defmodule AppWeb.ErrorViewTest do
use App.ConnCase, async: true use App.ConnCase, async: true
# Bring render/3 and render_to_string/3 for testing custom views # Bring render/3 and render_to_string/3 for testing custom views
import Phoenix.View import Phoenix.View
test "renders 404.html" do test "renders 404.html" do
assert render_to_string(App.ErrorView, "404.html", []) == "Not Found" assert render_to_string(AppWeb.ErrorView, "404.html", []) == "Not Found"
end end
test "renders 500.html" do test "renders 500.html" do
assert render_to_string(App.ErrorView, "500.html", []) == "Internal Server Error" assert render_to_string(AppWeb.ErrorView, "500.html", []) == "Internal Server Error"
end end
end end

View file

@ -24,7 +24,7 @@ defmodule App.ChannelCase do
import App.ChannelCase import App.ChannelCase
# The default endpoint for testing # The default endpoint for testing
@endpoint App.Endpoint @endpoint AppWeb.Endpoint
end end
end end

View file

@ -27,7 +27,7 @@ defmodule App.ConnCase do
alias App.Router.Helpers, as: Routes alias App.Router.Helpers, as: Routes
# The default endpoint for testing # The default endpoint for testing
@endpoint App.Endpoint @endpoint AppWeb.Endpoint
end end
end end

View file

@ -10,6 +10,7 @@ defmodule AuthWeb.Router do
plug :fetch_flash plug :fetch_flash
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end end
pipeline :api do pipeline :api do

View file

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title><%= I18n.t! "en", "site.title" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
<style type="text/css">
body {
background-color: #DADADA;
}
main > .grid {
height: 100vh;
}
.image {
margin-top: -100px;
}
.column {
max-width: 450px;
}
</style>
<main role="main" class="container">
<%= @inner_content %>
</main>
</body>
</html>

View file

@ -8,6 +8,7 @@ defmodule Content.Router do
plug :fetch_flash plug :fetch_flash
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end end
pipeline :api do pipeline :api do
@ -38,8 +39,6 @@ defmodule Content.Router do
put "/posts/preview", PostsController, :preview put "/posts/preview", PostsController, :preview
post "/posts/preview", PostsController, :preview post "/posts/preview", PostsController, :preview
get "/menus/:id/edit", MenusController, :edit
put "/menus/:id", MenusController, :update
end end
scope "/", Content do scope "/", Content do

View file

@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title><%= I18n.t! "en", "site.title" %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body>
<main role="main" class="container">
<%= render "_side_menu.html", assigns %>
<!-- Page Contents -->
<div class="pusher">
<div class="ui inverted vertical masthead center aligned segment">
<%= render "_menu.html", assigns %>
</div>
<%= flash_block(@conn) %>
<%= @inner_content %>
</div>
</main>
</body>
</html>

View file

@ -0,0 +1,8 @@
// Menu buttons get a little spacing
.masthead .ui.menu .ui.button {
margin-left: 0.5em;
}
[class*="top padded"] {
padding-top: 4em;
}

View file

@ -68,6 +68,7 @@ defmodule CoreWeb do
import Phoenix.View import Phoenix.View
import CoreWeb.ErrorHelpers import CoreWeb.ErrorHelpers
import CoreWeb.Helpers
import CoreWeb.Gettext import CoreWeb.Gettext
alias CoreWeb.Router.Helpers, as: Routes alias CoreWeb.Router.Helpers, as: Routes
end end

View file

@ -7,6 +7,7 @@ defmodule CoreWeb.Router do
plug :fetch_flash plug :fetch_flash
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end end
pipeline :api do pipeline :api do

View file

@ -9,12 +9,12 @@
<% end %> <% end %>
<div class="right item"> <div class="right item">
<%= if Pow.Plug.current_user(@conn) do %> <%= if Pow.Plug.current_user(@conn) do %>
<%= link "Sign out", to: AuthWeb.Router.Helpers.pow_session_path(@conn, :delete), method: :delete, class: "ui inverted button" %> <%= link "Sign out", to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :delete), method: :delete, class: "ui inverted button" %>
<% else %> <% else %>
<%= link to: AuthWeb.Router.Helpers.pow_session_path(@conn, :new), class: "ui inverted button" do %> <%= link to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :new), class: "ui inverted button" do %>
Log in Log in
<% end %> <% end %>
<%= link to: AuthWeb.Router.Helpers.pow_registration_path(@conn, :new), class: "ui inverted button" do %> <%= link to: AuthWeb.Router.Helpers.pow_registration_path(%URI{path: "/auth"}, :new), class: "ui inverted button" do %>
Sign Up Sign Up
<% end %> <% end %>
<% end %> <% end %>

View file

@ -4,12 +4,12 @@
<a class="item" href="/admin">Admin</a> <a class="item" href="/admin">Admin</a>
<% end %> <% end %>
<%= if Pow.Plug.current_user(@conn) do %> <%= if Pow.Plug.current_user(@conn) do %>
<%= link "Sign out", to: AuthWeb.Router.Helpers.pow_session_path(@conn, :delete), method: :delete, class: "item" %> <%= link "Sign out", to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :delete), method: :delete, class: "item" %>
<% else %> <% else %>
<%= link to: AuthWeb.Router.Helpers.pow_session_path(@conn, :new), class: "item" do %> <%= link to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :new), class: "item" do %>
Log in Log in
<% end %> <% end %>
<%= link to: AuthWeb.Router.Helpers.pow_registration_path(@conn, :new), class: "item" do %> <%= link to: AuthWeb.Router.Helpers.pow_registration_path(%URI{path: "/auth"}, :new), class: "item" do %>
Sign Up Sign Up
<% end %> <% end %>
<% end %> <% end %>

View file

@ -10,9 +10,18 @@
</head> </head>
<body> <body>
<main role="main" class="container"> <main role="main" class="container">
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p> <%= render "_side_menu.html", assigns %>
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<%= @inner_content %> <!-- Page Contents -->
<div class="pusher">
<div class="ui inverted vertical masthead center aligned segment">
<%= render "_menu.html", assigns %>
</div>
<%= flash_block(@conn) %>
<%= @inner_content %>
</div>
</main> </main>
</body> </body>
</html> </html>

View file

@ -27,16 +27,17 @@ defmodule CoreWeb.Helpers do
""" """
end end
def styled_input(f, field, opts \\ []) do def styled_input(f, field, opts \\ [], options \\ nil) do
styled_input(f, field, opts) do styled_input(f, field, opts, options) do
"" ""
end end
end end
def styled_input(f, field, opts, do: content) do def styled_input(f, field, opts, options, do: content) do
{icon, rest_opts} = Keyword.pop(opts, :icon, "") {icon, rest_opts} = Keyword.pop(opts, :icon, "")
{classes, rest_opts} = Keyword.pop(rest_opts, :class, "") {classes, rest_opts} = Keyword.pop(rest_opts, :class, "")
{label_text, rest_opts} = Keyword.pop(rest_opts, :label) {label_text, rest_opts} = Keyword.pop(rest_opts, :label)
{input_helper, rest_opts} = Keyword.pop(rest_opts, :input_helper, :text_input)
~E""" ~E"""
<div class="field <%= error_class(f, field) %>"> <div class="field <%= error_class(f, field) %>">
<%= if label_text do %> <%= if label_text do %>
@ -47,7 +48,11 @@ defmodule CoreWeb.Helpers do
<div class="ui left icon <%= classes %> input"> <div class="ui left icon <%= classes %> input">
<i class="<%= icon %> icon"></i> <i class="<%= icon %> icon"></i>
<%= text_input f, field, rest_opts %> <%= if options == nil do %>
<%= apply(Phoenix.HTML.Form, input_helper, [f, field, rest_opts]) %>
<% else %>
<%= apply(Phoenix.HTML.Form, input_helper, [f, field, options, rest_opts]) %>
<% end %>
<%= content %> <%= content %>
</div> </div>
<%= error_tag f, field, class: "ui pointing red basic label" %> <%= error_tag f, field, class: "ui pointing red basic label" %>

View file

@ -0,0 +1,35 @@
defmodule Mix.Legendary do
alias Mix.Phoenix.{Schema}
@doc false
def inputs(%Schema{} = schema) do
Enum.map(schema.attrs, fn
{_, {:references, _}} ->
nil
{key, :integer} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :number_input %>)
{key, :float} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :number_input, step: "any" %>)
{key, :decimal} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :number_input, step: "any" %>)
{key, :boolean} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :checkbox %>)
{key, :text} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :textarea %>)
{key, :date} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :date_select %>)
{key, :time} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :time_select %>)
{key, :utc_datetime} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :datetime_select %>)
{key, :naive_datetime} ->
~s(<%= styled_input f, #{inspect(key)}, input_helper: :datetime_select %>)
{key, {:array, :integer}} ->
~s(<%= styled_input f, #{inspect(key)}, [input_helper: :multiple_select], ["1": 1, "2": 2] %>)
{key, {:array, _}} ->
~s(<%= styled_input f, #{inspect(key)}, [input_helper: :multiple_select], ["Option 1": "option1", "Option 2": "option2"] %>)
{key, _} ->
~s(<%= styled_input f, #{inspect(key)} %>)
end)
end
end

View file

@ -1,5 +1,5 @@
defmodule <%= module %>Channel do defmodule <%= module %>Channel do
use <%= web_module %>, :channel use <%= module %>, :channel
@impl true @impl true
def join("<%= singular %>:lobby", payload, socket) do def join("<%= singular %>:lobby", payload, socket) do

View file

@ -1,9 +1,9 @@
defmodule <%= module %>ChannelTest do defmodule <%= module %>ChannelTest do
use <%= web_module %>.ChannelCase use <%= module %>.ChannelCase
setup do setup do
{:ok, _, socket} = {:ok, _, socket} =
<%= web_module %>.UserSocket <%= module %>.UserSocket
|> socket("user_id", %{some: :assign}) |> socket("user_id", %{some: :assign})
|> subscribe_and_join(<%= module %>Channel, "<%= singular %>:lobby") |> subscribe_and_join(<%= module %>Channel, "<%= singular %>:lobby")

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Controller do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Controller do
use <%= inspect context.web_module %>, :controller use <%= inspect context.module %>, :controller
alias <%= inspect context.module %> alias <%= inspect context.module %>
alias <%= inspect schema.module %> alias <%= inspect schema.module %>

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>ControllerTest do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>ControllerTest do
use <%= inspect context.web_module %>.ConnCase use <%= inspect context.module %>.ConnCase
import <%= inspect context.module %>Fixtures import <%= inspect context.module %>Fixtures

View file

@ -1,3 +1,3 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>View do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>View do
use <%= inspect context.web_module %>, :view use <%= inspect context.module %>, :view
end end

View file

@ -1,11 +1,11 @@
defmodule <%= inspect context.web_module %>.ChangesetView do defmodule <%= inspect context.module %>.ChangesetView do
use <%= inspect context.web_module %>, :view use <%= inspect context.module %>, :view
@doc """ @doc """
Traverses and translates changeset errors. Traverses and translates changeset errors.
See `Ecto.Changeset.traverse_errors/2` and See `Ecto.Changeset.traverse_errors/2` and
`<%= inspect context.web_module %>.ErrorHelpers.translate_error/1` for more details. `<%= inspect context.module %>.ErrorHelpers.translate_error/1` for more details.
""" """
def translate_errors(changeset) do def translate_errors(changeset) do
Ecto.Changeset.traverse_errors(changeset, &translate_error/1) Ecto.Changeset.traverse_errors(changeset, &translate_error/1)

View file

@ -1,10 +1,10 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Controller do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Controller do
use <%= inspect context.web_module %>, :controller use <%= inspect context.module %>, :controller
alias <%= inspect context.module %> alias <%= inspect context.module %>
alias <%= inspect schema.module %> alias <%= inspect schema.module %>
action_fallback <%= inspect context.web_module %>.FallbackController action_fallback <%= inspect context.module %>.FallbackController
def index(conn, _params) do def index(conn, _params) do
<%= schema.plural %> = <%= inspect context.alias %>.list_<%= schema.plural %>() <%= schema.plural %> = <%= inspect context.alias %>.list_<%= schema.plural %>()

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>ControllerTest do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>ControllerTest do
use <%= inspect context.web_module %>.ConnCase use <%= inspect context.module %>.ConnCase
import <%= inspect context.module %>Fixtures import <%= inspect context.module %>Fixtures

View file

@ -1,16 +1,16 @@
defmodule <%= inspect context.web_module %>.FallbackController do defmodule <%= inspect context.module %>.FallbackController do
@moduledoc """ @moduledoc """
Translates controller action results into valid `Plug.Conn` responses. Translates controller action results into valid `Plug.Conn` responses.
See `Phoenix.Controller.action_fallback/1` for more details. See `Phoenix.Controller.action_fallback/1` for more details.
""" """
use <%= inspect context.web_module %>, :controller use <%= inspect context.module %>, :controller
<%= if schema.generate? do %># This clause handles errors returned by Ecto's insert/update/delete. <%= if schema.generate? do %># This clause handles errors returned by Ecto's insert/update/delete.
def call(conn, {:error, %Ecto.Changeset{} = changeset}) do def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
conn conn
|> put_status(:unprocessable_entity) |> put_status(:unprocessable_entity)
|> put_view(<%= inspect context.web_module %>.ChangesetView) |> put_view(<%= inspect context.module %>.ChangesetView)
|> render("error.json", changeset: changeset) |> render("error.json", changeset: changeset)
end end
@ -18,7 +18,7 @@ defmodule <%= inspect context.web_module %>.FallbackController do
def call(conn, {:error, :not_found}) do def call(conn, {:error, :not_found}) do
conn conn
|> put_status(:not_found) |> put_status(:not_found)
|> put_view(<%= inspect context.web_module %>.ErrorView) |> put_view(<%= inspect context.module %>.ErrorView)
|> render(:"404") |> render(:"404")
end end
end end

View file

@ -1,6 +1,6 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>View do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>View do
use <%= inspect context.web_module %>, :view use <%= inspect context.module %>, :view
alias <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>View alias <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>View
def render("index.json", %{<%= schema.plural %>: <%= schema.plural %>}) do def render("index.json", %{<%= schema.plural %>: <%= schema.plural %>}) do
%{data: render_many(<%= schema.plural %>, <%= inspect schema.alias %>View, "<%= schema.singular %>.json")} %{data: render_many(<%= schema.plural %>, <%= inspect schema.alias %>View, "<%= schema.singular %>.json")}

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent do
use <%= inspect context.web_module %>, :live_component use <%= inspect context.module %>, :live_component
alias <%= inspect context.module %> alias <%= inspect context.module %>

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.Index do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.Index do
use <%= inspect context.web_module %>, :live_view use <%= inspect context.module %>, :live_view
alias <%= inspect context.module %> alias <%= inspect context.module %>
alias <%= inspect schema.module %> alias <%= inspect schema.module %>

View file

@ -1,7 +1,7 @@
<h1>Listing <%= schema.human_plural %></h1> <h1>Listing <%= schema.human_plural %></h1>
<%%= if @live_action in [:new, :edit] do %> <%%= if @live_action in [:new, :edit] do %>
<%%= live_modal @socket, <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent, <%%= live_modal @socket, <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent,
id: @<%= schema.singular %>.id || :new, id: @<%= schema.singular %>.id || :new,
title: @page_title, title: @page_title,
action: @live_action, action: @live_action,

View file

@ -1,15 +1,15 @@
defmodule <%= inspect context.web_module %>.LiveHelpers do defmodule <%= inspect context.module %>.LiveHelpers do
import Phoenix.LiveView.Helpers import Phoenix.LiveView.Helpers
@doc """ @doc """
Renders a component inside the `<%= inspect context.web_module %>.ModalComponent` component. Renders a component inside the `<%= inspect context.module %>.ModalComponent` component.
The rendered modal receives a `:return_to` option to properly update The rendered modal receives a `:return_to` option to properly update
the URL when the modal is closed. the URL when the modal is closed.
## Examples ## Examples
<%%= live_modal @socket, <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent, <%%= live_modal @socket, <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent,
id: @<%= schema.singular %>.id || :new, id: @<%= schema.singular %>.id || :new,
action: @live_action, action: @live_action,
<%= schema.singular %>: @<%= schema.singular %>, <%= schema.singular %>: @<%= schema.singular %>,
@ -18,6 +18,6 @@ defmodule <%= inspect context.web_module %>.LiveHelpers do
def live_modal(socket, component, opts) do def live_modal(socket, component, opts) do
path = Keyword.fetch!(opts, :return_to) path = Keyword.fetch!(opts, :return_to)
modal_opts = [id: :modal, return_to: path, component: component, opts: opts] modal_opts = [id: :modal, return_to: path, component: component, opts: opts]
live_component(socket, <%= inspect context.web_module %>.ModalComponent, modal_opts) live_component(socket, <%= inspect context.module %>.ModalComponent, modal_opts)
end end
end end

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>LiveTest do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>LiveTest do
use <%= inspect context.web_module %>.ConnCase use <%= inspect context.module %>.ConnCase
import Phoenix.LiveViewTest import Phoenix.LiveViewTest
import <%= inspect context.module %>Fixtures import <%= inspect context.module %>Fixtures

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.ModalComponent do defmodule <%= inspect context.module %>.ModalComponent do
use <%= inspect context.web_module %>, :live_component use <%= inspect context.module %>, :live_component
@impl true @impl true
def render(assigns) do def render(assigns) do

View file

@ -1,5 +1,5 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.Show do defmodule <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.Show do
use <%= inspect context.web_module %>, :live_view use <%= inspect context.module %>, :live_view
alias <%= inspect context.module %> alias <%= inspect context.module %>

View file

@ -1,7 +1,7 @@
<h1>Show <%= schema.human_singular %></h1> <h1>Show <%= schema.human_singular %></h1>
<%%= if @live_action in [:edit] do %> <%%= if @live_action in [:edit] do %>
<%%= live_modal @socket, <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent, <%%= live_modal @socket, <%= inspect context.module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>Live.FormComponent,
id: @<%= schema.singular %>.id, id: @<%= schema.singular %>.id,
title: @page_title, title: @page_title,
action: @live_action, action: @live_action,

View file

@ -1,9 +1,5 @@
use Mix.Config use Mix.Config
config :app,
ecto_repos: [App.Repo],
generators: [context_app: false]
# Configures the endpoint # Configures the endpoint
config :app, App.Endpoint, config :app, App.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],
@ -17,7 +13,8 @@ config :admin,
generators: [context_app: false] generators: [context_app: false]
config :app, config :app,
ecto_repos: [App.Repo] ecto_repos: [App.Repo],
generators: [context_app: :app]
# Configures the endpoint # Configures the endpoint
config :admin, Admin.Endpoint, config :admin, Admin.Endpoint,
@ -27,6 +24,14 @@ config :admin, Admin.Endpoint,
pubsub_server: Admin.PubSub, pubsub_server: Admin.PubSub,
live_view: [signing_salt: "g5ltUbnQ"] live_view: [signing_salt: "g5ltUbnQ"]
# Configures the endpoint
config :app, AppWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "r2eN53mJ9RmlGz9ZQ7xf43P3Or59aaO9rdf5D3hRcsuiH44pGW9kPGfl5mt9N1Gi",
render_errors: [view: AppWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: AppWeb.PubSub,
live_view: [signing_salt: "g5ltUbnQ"]
# Configure Mix tasks and generators # Configure Mix tasks and generators
config :auth, config :auth,
ecto_repos: [Auth.Repo] ecto_repos: [Auth.Repo]
@ -67,7 +72,7 @@ config :core,
{Content.Router, "/pages"}, {Content.Router, "/pages"},
{AuthWeb.Router, "/auth"}, {AuthWeb.Router, "/auth"},
{Admin.Router, "/admin"}, {Admin.Router, "/admin"},
{App.Router, "/app"}, {AppWeb.Router, "/app"},
], ],
email_from: "example@example.org" email_from: "example@example.org"
@ -77,7 +82,7 @@ config :content,
config :content, Content.Endpoint, server: false config :content, Content.Endpoint, server: false
config :auth_web, AuthWeb.Endpoint, server: false config :auth_web, AuthWeb.Endpoint, server: false
config :admin, Admin.Endpoint, server: false config :admin, Admin.Endpoint, server: false
config :app, App.Endpoint, server: false config :app, AppWeb.Endpoint, server: false
import_config "../apps/*/config/config.exs" import_config "../apps/*/config/config.exs"