Created
May 27, 2023 16:13
-
-
Save mrcwinn/869a3c9d297ac25e9047c7ce8f8007fb to your computer and use it in GitHub Desktop.
RaceSearchLive.ex
This file contains 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 OutandbackWeb.RaceSearchLive do | |
use OutandbackWeb, :live_view | |
import Ecto.Query | |
alias Outandback.Events.Race | |
@per_page 9 | |
def render(assigns) do | |
~H""" | |
<div class="bg-white px-6 lg:px-8"> | |
<div class="mx-auto max-w-2xl md:text-center"> | |
<h2 class="text-3xl font-bold tracking-tight text-gray-900 md:text-5xl">Race Research</h2> | |
<p class="text-xl font-medium tracking-tight text-gray-600 mt-6">Turn any in-person event into a personalized training plan.</p> | |
</div> | |
</div> | |
<div class="mt-16"> | |
<form class="max-w-md mx-auto"> | |
<.input name="query" placeholder="Race name, place or distance..." value={@query} type="text" phx-change="set_query" class="w-full flex-auto rounded-md border-0 px-3.5 py-2 text-lg text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-sm sm:leading-6 mr-2" /> | |
</form> | |
<ul class="mt-12 lg:grid lg:grid-cols-3 lg:grid-rows-fr lg:gap-6"> | |
<%= for race <- @races do %> | |
<li> | |
<div class="h-full cursor-pointer block group relative before:absolute before:-inset-2.5 before:rounded-[20px] before:bg-gray-50 before:opacity-0 hover:before:opacity-100"> | |
<div class="relative aspect-[3/2] overflow-hidden bg-gray-100 rounded-lg ring-1 ring-gray-900/10"> | |
<a href={"/races/#{race.slug}"} class="group-hover:text-blue-600"> | |
<%= unless is_nil(race.latitude) or is_nil(race.longitude) do %> | |
<img | |
alt="Image" | |
width="600" | |
height="400" | |
src={"https://api.mapbox.com/styles/v1/mrcwinn/ckaetjl730v8r1im98p1751kj/static/#{race.longitude},#{race.latitude},14.42,0,60/300x200@2x?access_token=pk.eyJ1IjoibXJjd2lubiIsImEiOiI2RjA5b2R3In0.m5u4w0NYCOUaEvBWwYCR-g"} | |
class="h-auto max-w-full" | |
/> | |
<% end %> | |
</a> | |
<%= if length(race.distances) > 0 do %> | |
<div class="top-2 right-2 z-1 absolute font-medium"> | |
<div class="text-gray-900 text-sm space-x-2 pt-2 flex"> | |
<%= for distance <- race.distances do %> | |
<div class="bg-white ring-1 ring-gray-700 shadow rounded-lg py-1 px-2 text-xs"> | |
<%= distance.distance %><%= distance.distance_unit %> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
<% end %> | |
</div> | |
<div class="relative p-4 space-y-1 text-gray-600"> | |
<div> | |
<a href={"/races/#{race.slug}"} class="group-hover:text-blue-600"><%= race.title %></a> | |
</div> | |
<%= unless is_nil(race.city) or is_nil(race.state) do %> | |
<div class="text-sm"> | |
<%= race.city %>, <%= race.state %> | |
</div> | |
<% end %> | |
<%= unless is_nil(race.event_date) do %> | |
<div class="text-sm"> | |
<%= race.event_date |> Timex.format!("{0M}/{0D}/{YYYY}") %> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
</li> | |
<% end %> | |
</ul> | |
<nav | |
class="my-6 flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6" | |
aria-label="Pagination" | |
> | |
<div class="hidden sm:block"> | |
<p class="text-sm text-gray-700"> | |
Showing <span class="font-medium"><%= (@page - 1) * @per_page + 1 %></span> to <span class="font-medium"><%= (@page - 1) * @per_page + 9 %></span> of | |
<span class="font-medium"><%= @count %></span> results | |
</p> | |
</div> | |
<div class="flex flex-1 justify-between sm:justify-end"> | |
<button | |
phx-click="prev" | |
class="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" | |
> | |
Previous | |
</button> | |
<button | |
phx-click="next" | |
class="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50" | |
> | |
Next | |
</button> | |
</div> | |
</nav> | |
</div> | |
""" | |
end | |
def mount(_params, _session, socket) do | |
query = from r in Race, | |
limit: 12 | |
count = Outandback.Repo.aggregate(Race, :count) | |
assigns = [ | |
page: 1, | |
count: count, | |
per_page: @per_page, | |
query: "", | |
races: paginate(query) | |
] | |
{:ok, assign(socket, assigns)} | |
end | |
def handle_event("set_query", %{"query" => query_string}, socket) when query_string == "" do | |
query = build_query(query_string) | |
assigns = [ | |
page: 1, | |
query: "", | |
races: paginate(query) | |
] | |
{:noreply, assign(socket, assigns)} | |
end | |
def handle_event("set_query", %{"query" => query_string}, socket) when query_string != "" do | |
query = build_query(query_string) | |
assigns = [ | |
page: 1, | |
query: query_string, | |
races: paginate(query) | |
] | |
{:noreply, assign(socket, assigns)} | |
end | |
def handle_event("prev", _unsigned_params, socket) do | |
new_page = socket.assigns.page - 1 | |
new_page = max(new_page, 1) | |
query = build_query(socket.assigns.query) | |
assigns = [ | |
page: new_page, | |
races: paginate(query, new_page), | |
] | |
{:noreply, assign(socket, assigns)} | |
end | |
def handle_event("next", _unsigned_params, socket) do | |
new_page = socket.assigns.page + 1 | |
query = build_query(socket.assigns.query) | |
assigns = [ | |
page: new_page, | |
races: paginate(query, new_page), | |
] | |
{:noreply, assign(socket, assigns)} | |
end | |
defp build_query(query_string) do | |
case query_string do | |
"" -> | |
from r in Race | |
_ -> | |
from r in Race, | |
where: fragment("tsv @@ plainto_tsquery('english', ?) OR ? % ? OR ? % ? OR ? % ? OR immutable_array_to_string(?, ' ') % ?", | |
^query_string, | |
r.title, | |
^query_string, | |
r.city, | |
^query_string, | |
r.state, | |
^query_string, | |
r.distance_titles, | |
^query_string | |
) | |
end | |
end | |
defp paginate(query, page \\ 1) do | |
query | |
|> limit(@per_page) | |
|> offset(^((page - 1) * @per_page)) | |
|> Outandback.Repo.all() | |
|> Outandback.Repo.preload(:distances) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment