Last active
August 18, 2022 19:05
-
-
Save benjamin-thomas/53354549c2e15f02a6b89faf2ed16f58 to your computer and use it in GitHub Desktop.
PragmaticStudio Elixir/Phoenix/LiveView solutions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.AutocompleteLive do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Stores | |
alias LiveViewStudio.Cities | |
def mount(_params, _session, socket) do | |
socket = | |
assign(socket, | |
zip: "", | |
city: "", | |
cities_suggestion: [], | |
stores: [], | |
loading: false | |
) | |
{:ok, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Find a Store</h1> | |
<div id="search"> | |
<form phx-submit="zip-search"> | |
<input type="text" name="zip" value={@zip} | |
placeholder="Zip Code" | |
autocomplete="off" | |
readonly={@loading} | |
> | |
<button type="submit"> | |
<img src="images/search.svg"> | |
</button> | |
</form> | |
<form phx-change="suggest-cities" phx-submit="city-search"> | |
<input type="text" name="city" value={@city} | |
autofocus | |
list="cities" | |
placeholder="City" | |
autocomplete="off" | |
phx-debounce="200" | |
> | |
<datalist id="cities"> | |
<%= for city <- @cities_suggestion do %> | |
<option value={city}> | |
<% end %> | |
</datalist> | |
<button type="submit"> | |
<img src="images/search.svg"> | |
</button> | |
</form> | |
<%= if @loading do %> | |
<div class="loader"> | |
Loading... | |
</div> | |
<% end %> | |
<div class="stores"> | |
<ul> | |
<%= for store <- @stores do %> | |
<li> | |
<div class="first-line"> | |
<div class="name"> | |
<%= store.name %> | |
</div> | |
<div class="status"> | |
<%= if store.open do %> | |
<span class="open">Open</span> | |
<% else %> | |
<span class="closed">Closed</span> | |
<% end %> | |
</div> | |
</div> | |
<div class="second-line"> | |
<div class="street"> | |
<img src="images/location.svg"> | |
<%= store.street %> | |
</div> | |
<div class="phone_number"> | |
<img src="images/phone.svg"> | |
<%= store.phone_number %> | |
</div> | |
</div> | |
</li> | |
<% end %> | |
</ul> | |
</div> | |
</div> | |
""" | |
end | |
def handle_event("zip-search", %{"zip" => zip}, socket) do | |
send(self(), {:run_zip_search, zip}) | |
socket = | |
assign(socket, | |
zip: zip, | |
city: "", | |
stores: [], | |
loading: true | |
) | |
{:noreply, socket} | |
end | |
def handle_event("suggest-cities", %{"city" => city}, socket) do | |
send(self(), {:suggest_cities, city}) | |
socket = | |
assign(socket, | |
zip: "", | |
city: city, | |
stores: [], | |
loading: true | |
) | |
{:noreply, socket} | |
end | |
def handle_event("city-search", %{"city" => city}, socket) do | |
send(self(), {:run_city_search, city}) | |
socket = | |
assign(socket, | |
city: city, | |
stores: [], | |
loading: true | |
) | |
{:noreply, socket} | |
end | |
def handle_info({:run_zip_search, zip}, socket) do | |
socket = | |
case Stores.search_by_zip(zip) do | |
[] = none -> | |
socket | |
|> put_flash(:info, "No stores matching \"#{zip}\"") | |
|> assign(stores: none, loading: false) | |
stores -> | |
assign(socket, stores: stores, loading: false) | |
end | |
{:noreply, socket} | |
end | |
def handle_info({:suggest_cities, prefix}, socket) do | |
socket = | |
case Cities.suggest(prefix) do | |
[] = none -> | |
socket | |
|> assign(cities_suggestion: none, loading: false) | |
cities -> | |
assign(socket, cities_suggestion: cities, loading: false) | |
end | |
{:noreply, socket} | |
end | |
def handle_info({:run_city_search, city}, socket) do | |
socket = | |
case Stores.search_by_city(city) do | |
[] = none -> | |
socket | |
|> put_flash(:info, "No stores matching \"#{city}\"") | |
|> assign(stores: none) | |
stores -> | |
assign(socket, stores: stores) |> clear_flash | |
end | |
|> assign(cities_suggestion: [], loading: false) | |
{:noreply, socket} | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.LicenseLive do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Licenses | |
import Number.Currency | |
def mount(_params, _session, socket) do | |
seats = 2 | |
socket = assign(socket, seats: seats, amount: Licenses.calculate(seats)) | |
{:ok, socket} | |
end | |
def handle_event("change", %{"seats" => seats}, socket) do | |
seats = String.to_integer(seats) | |
socket = assign(socket, seats: seats, amount: Licenses.calculate(seats)) | |
{:noreply, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Team Licenses</h1> | |
<div id="license"> | |
<div class="card"> | |
<div class="content"> | |
<div class="seats"> | |
<img src="images/license.svg" alt=""> | |
<span> | |
Your license is currently for <strong><%= @seats %></strong> seats. | |
</span> | |
</div> | |
<form phx-change="change"> | |
<input type="range" min="1" max="10" name="seats" value={@seats}> | |
</form> | |
<div class="amount"> | |
<%= number_to_currency @amount %> | |
</div> | |
</div> | |
</div> | |
</div> | |
""" | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.FilterLive do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Boats | |
def mount(_params, _session, socket) do | |
socket = init(socket) | |
{:ok, socket, temporary_assigns: [boats: []]} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Daily Boat Rentals</h1> | |
<div id="filter"> | |
<form phx-change="filter"> | |
<div class="filters"> | |
<select name="type" id="type"> | |
<%= options_for_select(type_options(), @type) %> | |
</select> | |
<div class="prices"> | |
<%= for price <- ["$", "$$", "$$$"] do %> | |
<%= price_checkbox(%{price: price, checked: price in @prices}) %> | |
<% end %> | |
</div> | |
<a phx-click="clear-all" href="javascript:void(0)">Clear All</a> | |
</div> | |
</form> | |
<div class="boats"> | |
<%= for boat <- @boats do %> | |
<div class="card"> | |
<img src={boat.image} alt="boat"> | |
<div class="content"> | |
<div class="model"><%= boat.model %></div> | |
<div class="details"> | |
<span class="price"><%= boat.price %></span> | |
<span class="type"><%= boat.type %></span> | |
</div> | |
</div> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
""" | |
end | |
def handle_event("filter", %{"type" => type, "prices" => prices}, socket) do | |
params = [type: type, prices: prices] | |
boats = Boats.list_boats(params) | |
socket = assign(socket, params ++ [boats: boats]) | |
{:noreply, socket} | |
end | |
def handle_event("filter", %{"type" => type}, socket) do | |
handle_event("filter", %{"type" => type, "prices" => []}, socket) | |
end | |
def handle_event("clear-all", _, socket) do | |
socket = init(socket) | |
{:noreply, socket} | |
end | |
defp init(socket) do | |
assign(socket, | |
boats: Boats.list_boats(), | |
type: "", | |
prices: [] | |
) | |
end | |
defp type_options do | |
[ | |
"All types": "", | |
Fishing: "fishing", | |
Sporting: "sporting", | |
Sailing: "sailing" | |
] | |
end | |
defp price_checkbox(assigns) do | |
~H""" | |
<input | |
checked={@checked} | |
type="checkbox" name="prices[]" id={@price} value={@price}> | |
<label for={@price}><%= @price %></label> | |
""" | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.FlightsLive do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Flights | |
def mount(_params, _session, socket) do | |
socket = | |
assign(socket, | |
loading: false, | |
q: "", | |
flights: Flights.list_flights() | |
) | |
{:ok, socket} | |
end | |
def handle_event("submit", %{"q" => q}, socket) do | |
send(self(), {:search_flights2, q}) | |
socket = | |
assign(socket, | |
loading: true, | |
q: q, | |
flights: [] | |
) | |
{:noreply, socket} | |
end | |
def handle_info({:search_flights, number}, socket) do | |
flights = Flights.search_by_number(number) | |
socket = | |
assign(socket, | |
loading: false, | |
flights: flights | |
) | |
socket = | |
if flights == [] do | |
put_flash(socket, :error, "No flights found with that number") | |
else | |
clear_flash(socket) | |
end | |
{:noreply, socket} | |
end | |
def handle_info({:search_flights2, number}, socket) do | |
socket = | |
case Flights.search_by_number(number) do | |
[] = empty -> | |
socket | |
|> put_flash(:info, "No flights found with number \"#{number}\"") | |
|> assign(flights: empty, loading: false) | |
flights -> | |
socket | |
|> clear_flash() | |
|> assign(flights: flights, loading: false) | |
end | |
{:noreply, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Find a Flight</h1> | |
<div id="search"> | |
<!-- NOTE: autofocus keeps working only if set before any assigns --> | |
<form phx-submit="submit" autocomplete="off"> | |
<input type="text" name="q" value={@q} readonly={@loading} autofocus> | |
<button type="submit"> | |
<img src="images/search.svg" alt="search"> | |
</button> | |
</form> | |
<%= if @loading do %> | |
<div class="loader">Loading...</div> | |
<% end %> | |
<div class="flights"> | |
<ul> | |
<%= for flight <- @flights do %> | |
<li> | |
<div class="first-line"> | |
<div class="number"> | |
Flight #<%= flight.number %> | |
</div> | |
<div class="origin-destination"> | |
<img src="images/location.svg"> | |
<%= flight.origin %> to | |
<%= flight.destination %> | |
</div> | |
</div> | |
<div class="second-line"> | |
<div class="departs"> | |
Departs: <%= flight.departure_time |> format_time %> | |
</div> | |
<div class="arrives"> | |
Arrives: <%= flight.arrival_time |> format_time %> | |
</div> | |
</div> | |
</li> | |
<% end %> | |
</ul> | |
</div> | |
</div> | |
""" | |
end | |
defp format_time(time) do | |
Timex.format!(time, "%b %d at %H:%M", :strftime) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.LightLive do | |
use LiveViewStudioWeb, :live_view | |
def mount(_params, _session, socket) do | |
socket = | |
socket | |
|> assign(:brightness, 10) | |
|> assign(:inc, 5) | |
|> assign(:temp, "4000") | |
{:ok, socket} | |
end | |
def handle_event("on", _, socket) do | |
socket = assign(socket, :brightness, 100) | |
{:noreply, socket} | |
end | |
def handle_event("off", _, socket) do | |
socket = assign(socket, :brightness, 0) | |
{:noreply, socket} | |
end | |
def handle_event("up", _, socket) do | |
# brightness = socket.assigns.brightness + 10 | |
# socket = assign(socket, :brightness, brightness) | |
inc = socket.assigns.inc | |
IO.inspect("--> inc: #{inc}") | |
socket = update(socket, :brightness, fn n -> min(n + inc, 100) end) | |
{:noreply, socket} | |
end | |
def handle_event("down", _, socket) do | |
inc = socket.assigns.inc | |
socket = update(socket, :brightness, fn n -> max(n - inc, 0) end) | |
{:noreply, socket} | |
end | |
def handle_event("keyup", %{"key" => "ArrowUp"}, socket) do | |
handle_event("up", nil, socket) | |
end | |
def handle_event("keyup", %{"key" => "PageUp"}, socket) do | |
handle_event("on", nil, socket) | |
end | |
def handle_event("keyup", %{"key" => "ArrowDown"}, socket) do | |
handle_event("down", nil, socket) | |
end | |
def handle_event("keyup", %{"key" => "PageDown"}, socket) do | |
handle_event("off", nil, socket) | |
end | |
def handle_event("keyup", %{"key" => key}, socket) do | |
IO.inspect("NOOP for key: #{key}") | |
{:noreply, socket} | |
end | |
def handle_event("rand", _, socket) do | |
# socket = update(socket, :brightness, fn _ -> Enum.random(0..100) end) | |
socket = assign(socket, :brightness, Enum.random(0..100)) | |
{:noreply, socket} | |
end | |
def handle_event("submit", %{"inc" => inc}, socket) do | |
{inc, ""} = Integer.parse(inc) | |
IO.inspect("--> inc: #{inc}") | |
{:noreply, assign(socket, :inc, inc)} | |
end | |
def handle_event("change-temp", %{"temp" => temp}, socket) do | |
socket = assign(socket, temp: temp) | |
{:noreply, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Front Porch Light</h1> | |
<div id="light" phx-window-keyup="keyup"> | |
<div class="meter"> | |
<span style={"width: #{@brightness}%;background-color:#{temp_color(@temp)}"}> | |
<%= @brightness %>% | |
</span> | |
</div> | |
<button phx-click="off"><img src="images/light-off.svg"></button> | |
<button phx-click="down"><img src="images/down.svg"></button> | |
<button phx-click="up"><img src="images/up.svg"></button> | |
<button phx-click="on"><img src="images/light-on.svg"></button> | |
<br> | |
<button phx-click="rand">RAND</button> | |
<br> | |
<form phx-change="submit"> | |
<!-- | |
<input name="inc" type="number" min="1" max="10" value={@inc}> | |
--> | |
<select name="inc"> | |
<%= for i <- [1,5,10] do %> | |
<option | |
selected={if i==@inc do "" else nil end} | |
value={i}> | |
<%= i %> | |
</option> | |
<% end %> | |
</select> | |
</form> | |
<p>Increase by <%= @inc %></p> | |
<form phx-change="change-temp"> | |
<%= for color <- ["3000", "4000", "5000"] do %> | |
<%= temp_radio_button(%{color: color, checked: @temp == color}) %> | |
<% end %> | |
</form> | |
</div> | |
""" | |
end | |
# Write a function named temp_radio_button that generates a radio button and label combo. | |
defp temp_radio_button(assigns) do | |
~H""" | |
<input type="radio" id={@color} name="temp" value={@color} checked={@checked}> | |
<label for={@color}><%= @color %></label> | |
""" | |
end | |
defp temp_color("3000"), do: "#F1C40D" | |
defp temp_color("4000"), do: "#FEFF66" | |
defp temp_color("5000"), do: "#99CCFF" | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.SalesDashboard do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Sales | |
def mount(_params, _session, socket) do | |
if connected?(socket) do | |
:timer.send_interval(2000, self(), :tick) | |
end | |
{:ok, assign_stats(socket)} | |
end | |
def handle_event("refresh", _, socket) do | |
{:noreply, assign_stats(socket)} | |
end | |
def handle_info(:tick, socket) do | |
handle_event("refresh", nil, socket) | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Sales Dashboard</h1> | |
<div id="dashboard"> | |
<div class="stats"> | |
<div class="stat"> | |
<span class="value"><%= @new_orders %></span> | |
<span class="name">New Orders</span> | |
</div> | |
<div class="stat"> | |
<span class="value"><%= @sales_amount %></span> | |
<span class="name">Sales Amount</span> | |
</div> | |
<div class="stat"> | |
<span class="value"><%= @satisfaction %></span> | |
<span class="name">Satisfaction</span> | |
</div> | |
</div> | |
<button phx-click="refresh"> | |
<img src="images/refresh.svg" alt="Refresh button"> | |
Refresh | |
</button> | |
</div> | |
""" | |
end | |
defp assign_stats(socket) do | |
assign(socket, | |
new_orders: Sales.new_orders(), | |
sales_amount: Sales.sales_amount(), | |
satisfaction: Sales.satisfaction() | |
) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.SearchLive do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Stores | |
def mount(_params, _session, socket) do | |
socket = assign(socket, loading: false, zip: "", stores: []) | |
{:ok, socket} | |
end | |
def handle_event("zip-search", %{"zip" => zip}, socket) do | |
send(self(), {:zip_search, zip}) | |
socket = assign(socket, zip: zip, loading: true, stores: []) | |
{:noreply, socket} | |
end | |
def handle_info({:zip_search, zip}, socket) do | |
stores = Stores.search_by_zip(zip) | |
socket = | |
if stores == [] do | |
put_flash( | |
socket, | |
:error, | |
"Oops, je n'ai pas trouvé de resto avec le code postal \"#{zip}\" !" | |
) | |
else | |
socket |> clear_flash() | |
end | |
{:noreply, assign(socket, stores: stores, loading: false)} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Find a store</h1> | |
<div id="search"> | |
<form phx-submit="zip-search" autocomplete="off"> | |
<input type="text" name="zip" placeholder="Zip Code - e.g. 59602, 80204" | |
value={@zip} | |
readonly={@loading} | |
autofocus> | |
<button type="submit"> | |
<img src="images/search.svg" alt="search icon"> | |
</button> | |
</form> | |
<%= if @loading do %> | |
<div class="loader"> | |
Loading... | |
</div> | |
<% end %> | |
<div class="stores"> | |
<ul> | |
<%= for store <- @stores do %> | |
<li> | |
<div class="first-line"> | |
<div class="name"> | |
<%= store.name %> | |
</div> | |
</div> | |
<div class="status"> | |
<%= if store.open do %> | |
<span class="open">Open</span> | |
<% else %> | |
<span class="closed">Closed</span> | |
<% end %> | |
</div> | |
<div class="second-line"> | |
<div class="street"> | |
<img src="images/location.svg" alt="location"> | |
<%= store.street %> | |
</div> | |
<div class="phone_number"> | |
<img src="images/phone.svg" alt="phone"> | |
<%= store.phone_number %> | |
</div> | |
</div> | |
</li> | |
<% end %> | |
</ul> | |
</div> | |
</div> | |
""" | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewStudioWeb.ServersLive do | |
use LiveViewStudioWeb, :live_view | |
alias LiveViewStudio.Servers | |
def mount(_params, _session, socket) do | |
servers = Servers.list_servers() | |
socket = | |
assign(socket, | |
servers: servers, | |
selected_server: hd(servers) | |
) | |
{:ok, socket} | |
end | |
def handle_event("show", %{"id" => id}, socket) do | |
id = String.to_integer(id) | |
server = Servers.get_server!(id) | |
socket = assign(socket, selected_server: server, page_title: "SRV: #{server.name}") | |
{:noreply, socket} | |
end | |
# `handle_params` is invoked after `mount` and each time `live_patch` is used | |
def handle_params(%{"id" => _id} = params, _url, socket) do | |
handle_event("show", params, socket) | |
end | |
def handle_params(_params, _url, socket) do | |
{:noreply, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<h1>Servers</h1> | |
<div id="servers"> | |
<div class="sidebar"> | |
<nav> | |
<%= for server <- @servers do %> | |
<!-- | |
<a href="#" phx-click="show" phx-value-id={server.id} class={if server == @selected_server, do: "active"}"> | |
<img src="/images/server.svg"> | |
<#%= server.name %> | |
</a> | |
--> | |
<div> | |
<%= live_patch link_body(%{name: server.name}), | |
to: Routes.live_path(@socket, __MODULE__, id: server.id), | |
class: if server == @selected_server, do: "active" | |
%> | |
</div> | |
<% end %> | |
</nav> | |
</div> | |
<div class="main"> | |
<div class="wrapper"> | |
<div class="card"> | |
<div class="header"> | |
<h2><%= @selected_server.name %></h2> | |
<span class={@selected_server.status}> | |
<%= @selected_server.status %> | |
</span> | |
</div> | |
<div class="body"> | |
<div class="row"> | |
<div class="deploys"> | |
<img src="/images/deploy.svg"> | |
<span> | |
<%= @selected_server.deploy_count %> deploys | |
</span> | |
</div> | |
<span> | |
<%= @selected_server.size %> MB | |
</span> | |
<span> | |
<%= @selected_server.framework %> | |
</span> | |
</div> | |
<h3>Git Repo</h3> | |
<div class="repo"> | |
<%= @selected_server.git_repo %> | |
</div> | |
<h3>Last Commit</h3> | |
<div class="commit"> | |
<%= @selected_server.last_commit_id %> | |
</div> | |
<blockquote> | |
<%= @selected_server.last_commit_message %> | |
</blockquote> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
""" | |
end | |
defp link_body(assigns) do | |
~H""" | |
<img src="/images/server.svg"> | |
<%= @name %> | |
""" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment