Browse Source

added search

closes #5
pull/7/head
Victor Roest 2 years ago
parent
commit
98b168ea12
Signed by: 0x76 GPG Key ID: A3923C699D1A3BDA
  1. 2
      .credo.exs
  2. 1
      .gitattributes
  3. 12
      lib/dps/author.ex
  4. 9
      lib/dps/cache.ex
  5. 5
      lib/dps/poem.ex
  6. 8
      lib/dps_web/controllers/author_page_controller.ex
  7. 8
      lib/dps_web/controllers/poem_page_controller.ex
  8. 2
      lib/dps_web/telemetry.ex
  9. 14
      lib/dps_web/templates/author_page/index.html.eex
  10. 2
      lib/dps_web/templates/author_page/new.html.eex
  11. 6
      lib/dps_web/templates/layout/app.html.eex
  12. 6
      lib/dps_web/templates/poem_page/index.html.eex
  13. 2
      lib/dps_web/templates/poem_page/new.html.eex
  14. 9
      priv/repo/seeds.exs
  15. 88
      priv/static/css/app.css
  16. 24
      priv/static/css/font.css
  17. BIN
      priv/static/fonts/ddg-serp-icons.woff

2
.credo.exs

@ -23,7 +23,7 @@
#
included: [
"lib/",
"test/",
"test/"
],
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
},

1
.gitattributes

@ -1,2 +1,3 @@
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.woff filter=lfs diff=lfs merge=lfs -text

12
lib/dps/author.ex

@ -31,10 +31,14 @@ defmodule Dps.Author.Query do
|> Repo.insert()
end
def all_authors(sort_by \\ [asc: :name]) do
Author
|> order_by(^sort_by)
|> Repo.all
def all_authors(query \\ "", sort_by \\ [asc: :name]) do
wildcard_query = "%#{query}%"
from(a in Author,
order_by: ^sort_by,
where: ilike(a.name, ^wildcard_query)
)
|> Repo.all()
end
def get_author_by_id(id) do

9
lib/dps/cache.ex

@ -25,6 +25,10 @@ defmodule Dps.Cache do
GenServer.cast(DpsCache, {:delete, key})
end
def clear do
GenServer.cast(DpsCache, :clear)
end
### internal API
def handle_call({:get, key}, _from, state) do
reply =
@ -45,4 +49,9 @@ defmodule Dps.Cache do
:ets.delete(@cache_table, key)
{:noreply, state}
end
def handle_cast(:clear, state) do
:ets.delete_all_objects(@cache_table)
{:noreply, state}
end
end

5
lib/dps/poem.ex

@ -30,9 +30,12 @@ defmodule Dps.Poem.Query do
alias Dps.{Repo, Poem, Cache}
@spec get_all_poems :: nil | [%Poem{}]
def get_all_poems do
def get_all_poems(search \\ "") do
wildcard_search = "%#{search}%"
from(p in Poem,
select: %Poem{id: p.id, author_id: p.author_id, title: p.title},
where: ilike(p.title, ^wildcard_search),
order_by: [desc: p.id]
)
|> Repo.all()

8
lib/dps_web/controllers/author_page_controller.ex

@ -2,8 +2,10 @@ defmodule DpsWeb.AuthorPageController do
use DpsWeb, :controller
alias Dps.{Poem, Author}
def index(conn, _params) do
authors = Author.Query.all_authors()
def index(conn, params) do
query = get_in(params, ["query"])
authors = Author.Query.all_authors(query)
render(conn, "index.html", authors: authors, title: "Authors")
end
@ -22,7 +24,7 @@ defmodule DpsWeb.AuthorPageController do
end
def create(conn, params) do
%{"author" => author} = params
author = get_in(params, ["author"])
with {:ok, %Author{}} <- Author.Query.create_author(author) do
redirect(conn, to: Routes.author_page_path(conn, :index))

8
lib/dps_web/controllers/poem_page_controller.ex

@ -2,8 +2,10 @@ defmodule DpsWeb.PoemPageController do
use DpsWeb, :controller
alias Dps.{Poem, Author}
def index(conn, _params) do
poems = Poem.Query.get_all_poems()
def index(conn, params) do
query = get_in(params, ["query"])
poems = Poem.Query.get_all_poems(query)
render(conn, "index.html", poems: poems)
end
@ -26,7 +28,7 @@ defmodule DpsWeb.PoemPageController do
end
def create(conn, params) do
%{"poem" => poem} = params
poem = get_in(params, ["poem"])
with {:ok, %Poem{id: id}} <- Poem.Query.create_poem(poem) do
redirect(conn, to: Routes.poem_page_path(conn, :show, id))

2
lib/dps_web/telemetry.ex

@ -13,7 +13,7 @@ defmodule DpsWeb.Telemetry do
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000},
# Add reporters as children of your supervision tree
{TelemetryMetricsPrometheus, [metrics: metrics()]},
{TelemetryMetricsPrometheus, [metrics: metrics()]}
# {Telemetry.Metrics.ConsoleReporter, metrics: custom_metrics()},
]

14
lib/dps_web/templates/author_page/index.html.eex

@ -1,11 +1,15 @@
<nav>
<%= link "Poems", to: Routes.poem_page_path(@conn, :index) %> - <b>Authors</b>
<hr>
<%= link "Poems", to: Routes.poem_page_path(@conn, :index) %> - <b><%= link "Authors", to: Routes.author_page_path(@conn, :index) %></b>
<hr>
<%= form_for @conn, Routes.author_page_path(@conn, :index), [method: :get, class: "search"], fn f -> %>
<%= search_input f, :query, [placeholder: "search author"] %>
<%= submit "S" %>
<% end %>
</nav>
<ul>
<%= for author <- @authors do %>
<li>
<%= link author.name, to: Routes.author_page_path(@conn, :show, author.id) %>
</li>
<li>
<%= link author.name, to: Routes.author_page_path(@conn, :show, author.id) %>
</li>
<% end %>
</ul>

2
lib/dps_web/templates/author_page/new.html.eex

@ -1,6 +1,6 @@
<h1>Add Author</h1>
<%= form_for @changeset, Routes.author_page_path(@conn, :create), fn f -> %>
<%= form_for @changeset, Routes.author_page_path(@conn, :create), [class: "new"], fn f -> %>
<label for="author_name">Name:</label>
<%= text_input f, :name %>

6
lib/dps_web/templates/layout/app.html.eex

@ -4,8 +4,12 @@
<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><%= if assigns[:title] do @title else "Poems" end %></title>
<title><%= assigns[:title] || "Poems" %></title>
<meta name="description" content="A personal collection of poems" />
<link rel="preload" href="<%= Routes.static_path(@conn, "/fonts/ddg-serp-icons.woff") %>" as="font" crossorigin>
<link rel="preload" href="<%= Routes.static_path(@conn, "/fonts/FiraSans/FiraSans-Regular.otf") %>" as="font" crossorigin>
<link rel="preload" href="<%= Routes.static_path(@conn, "/fonts/FiraSans/FiraSans-Bold.otf") %>" as="font" crossorigin>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/font.css") %>"/>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
</head>
<body>

6
lib/dps_web/templates/poem_page/index.html.eex

@ -1,6 +1,10 @@
<nav>
<b>Poems</b> - <%= link "Authors", to: Routes.author_page_path(@conn, :index) %>
<b><%= link "Poems", to: Routes.poem_page_path(@conn, :index) %></b> - <%= link "Authors", to: Routes.author_page_path(@conn, :index) %>
<hr>
<%= form_for @conn, Routes.poem_page_path(@conn, :index), [method: :get, class: "search"], fn f -> %>
<%= search_input f, :query, [placeholder: "search poem"] %>
<%= submit "S" %>
<% end %>
</nav>
<dl role="list">

2
lib/dps_web/templates/poem_page/new.html.eex

@ -1,6 +1,6 @@
<h1>Add Poem</h1>
<%= form_for @changeset, Routes.poem_page_path(@conn, :create), fn f -> %>
<%= form_for @changeset, Routes.poem_page_path(@conn, :create), [class: "new"], fn f -> %>
<div>
<label for="poem_title">Title</label>

9
priv/repo/seeds.exs

@ -99,9 +99,9 @@ And that has made all the difference."
})
Repo.insert!(%Poem{
author_id: edgar_allan_poe,
title: "The Raven",
content: "Once upon a midnight dreary, while I pondered, weak and weary,
author_id: edgar_allan_poe,
title: "The Raven",
content: "Once upon a midnight dreary, while I pondered, weak and weary,
Over many a quaint and curious volume of forgotten lore
While I nodded, nearly napping, suddenly there came a tapping,
As of some one gently rapping, rapping at my chamber door.
@ -225,4 +225,5 @@ On the pallid bust of Pallas just above my chamber door;
And his eyes have all the seeming of a demons that is dreaming,
And the lamp-light oer him streaming throws his shadow on the floor;
And my soul from out that shadow that lies floating on the floor
Shall be liftednevermore!"})
Shall be liftednevermore!"
})

88
priv/static/css/app.css

@ -1,14 +1,14 @@
/* Defines 'Fira Sans' and 'Droid Serif' */
@import "font.css";
/* Variables */
:root {
--text: black;
--text-inverted: white;
--bg: #fdfdfd;
--secondary: #444;
--border: #ccc;
--accent: darkslateblue;
--secondary: darkslategray;
--secondary-light: lightgray;
--secondary-text: #2d2d2d;
--accent: #483d8b;
--accent-dark: #342c66;
--rad: .3rem;
}
/* Main Content */
@ -16,6 +16,11 @@ body {
background-color: var(--bg);
}
hr {
padding: 0;
margin: 0.3rem 0;
}
main.container {
display: flex;
flex-direction: column;
@ -68,7 +73,7 @@ dd {
margin-bottom: 1em;
}
ul {
dl, ul {
padding: 0;
margin: 0;
}
@ -103,50 +108,93 @@ blockquote {
pre {
white-space: pre;
font-family: 'Droid Serif';
font-family: 'Droid Serif', serif;
line-height: 1.4;
margin: 1em 0;
font-size-adjust: inherit;
}
/* Forms */
form {
/* New Poem/Author Form */
form.new {
width: 50rem;
max-width: 90%;
}
form label {
form.new label {
font-size: 1.2rem;
}
form span.deemphasize {
form.new span.deemphasize {
color: var(--secondary);
opacity: 0.9;
font-size: 1rem;
}
form input[type=text],
form select,
form textarea {
form.new select,
form.new input[type=text],
form.new textarea {
width: 100%;
padding: 1.2em 2em;
margin: 0.5em 0;
border: 1px solid var(--border);
border-radius: 4px;
border: 1px solid #ccc;
border-radius: var(--rad);
box-sizing: border-box;
}
form textarea {
form.new textarea {
height: 35vh;
}
form button[type="submit"] {
form.new button[type="submit"] {
width: 100%;
background-color: var(--accent);
color: var(--text-inverted);
padding: 1.2em 2em;
margin: 0.5em 0;
border: none;
border-radius: 4px;
border-radius: var(--rad);
cursor: pointer;
}
/* Search form */
form.search {
position: relative;
margin: 0 0 .3rem 0;
padding: 0;
}
form.search input, form.search button {
height: 2rem;
border: 0;
font-size: 1rem;
}
form.search input[type=search] {
appearance: none;
outline-width: thin;
width: calc(100% - 3rem);
background: var(--secondary-light);
padding: 0 0.5rem;
border-radius: var(--rad) 0 0 var(--rad);
position: relative;
color: var(--secondary-text);
}
form.search button[type="submit"] {
position: absolute;
bottom: 0;
right: 0;
font-family: ddg-serp-icons;
background: var(--accent);
width: 3rem;
border-radius: 0 var(--rad) var(--rad) 0;
cursor: pointer;
color: var(--text-inverted);
transition: all .15s ease-in;
transition-property: background;
}
form.search button[type="submit"]:hover {
background: var(--accent-dark);
}

24
priv/static/css/font.css

@ -3,27 +3,31 @@
font-family: 'Droid Serif';
font-style: normal;
font-weight: normal;
src: url("../fonts/DroidSerif/DroidSerif-Regular.ttf")
src: local('Droid Serif Regular'), local("DroidSerif-Regular"), url("../fonts/DroidSerif/DroidSerif-Regular.ttf");
font-display: swap;
}
@font-face {
font-family: 'Droid Serif';
font-style: normal;
font-weight: bold;
src: url("../fonts/DroidSerif/DroidSerif-Bold.ttf")
src: url("../fonts/DroidSerif/DroidSerif-Bold.ttf");
font-display: swap;
}
@font-face {
font-family: 'Droid Serif';
font-style: italic;
font-weight: normal;
src: url("../fonts/DroidSerif/DroidSerif-Italic.ttf")
font-display: swap;
src: url("../fonts/DroidSerif/DroidSerif-Italic.ttf");
}
@font-face {
font-family: 'Droid Serif';
font-style: italic;
font-weight: bold;
font-display: swap;
src: url("../fonts/DroidSerif/DroidSerif-BoldItalic.ttf")
}
@ -31,6 +35,7 @@
@font-face {
font-family: 'Fira Sans';
font-weight: bold;
font-display: swap;
font-style: normal;
src: url("../fonts/FiraSans/FiraSans-Bold.otf")
}
@ -38,6 +43,7 @@
@font-face {
font-family: 'Fira Sans';
font-weight: bold;
font-display: swap;
font-style: italic;
src: url("../fonts/FiraSans/FiraSans-BoldItalic.otf")
}
@ -45,6 +51,7 @@
@font-face {
font-family: 'Fira Sans';
font-weight: normal;
font-display: swap;
font-style: italic;
src: url("../fonts/FiraSans/FiraSans-Italic.otf")
}
@ -52,6 +59,7 @@
@font-face {
font-family: 'Fira Sans';
font-weight: 200;
font-display: swap;
font-style: normal;
src: url("../fonts/FiraSans/FiraSans-Light.otf")
}
@ -59,13 +67,21 @@
@font-face {
font-family: 'Fira Sans';
font-weight: 200;
font-display: swap;
font-style: italic;
src: url("../fonts/FiraSans/FiraSans-LightItalic.otf")
}
@font-face {
font-family: 'Fira Sans';
font-display: swap;
font-weight: normal;
font-style: normal;
src: url("../fonts/FiraSans/FiraSans-Regular.otf")
src: local("Fira Sans"), local("FiraSans-Regular"), local("Fira Sans Regular"), url("../fonts/FiraSans/FiraSans-Regular.otf")
}
@font-face {
font-family: ddg-serp-icons;
font-display: swap;
src: url("../fonts/ddg-serp-icons.woff")
}

BIN
priv/static/fonts/ddg-serp-icons.woff (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save