Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search makers by skills and location and show only verified makers #4

Open
wants to merge 20 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 76 additions & 14 deletions lib/maker_passport/maker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,56 @@ defmodule MakerPassport.Maker do
[%Profile{}, ...]

"""
def list_profiles(skills \\ []) do
Repo.all(Profile) |> Repo.preload([:skills, :user])

def list_profiles(filter_params \\ %{}) do
query =
Profile
|> join(:left, [p], s in assoc(p, :skills))
|> maybe_filter_by_skills(skills)
|> preload([:skills, :user])
|> distinct([p], p.id)
Profile
|> join(:left, [p], s in assoc(p, :skills))
|> join(:left, [p], l in assoc(p, :location))
|> join(:left, [p], u in assoc(p, :user))
|> where([p, ..., u], not is_nil(u.confirmed_at))
|> maybe_filter_by_country(filter_params)
|> maybe_filter_by_city(filter_params)
|> maybe_filter_by_skills(filter_params)
|> preload([:skills, :user])
|> distinct([p], p.id)

Repo.all(query)
end

defp maybe_filter_by_skills(query, []), do: query

defp maybe_filter_by_skills(query, skills) do
defp maybe_filter_by_skills(query, %{search_skills: skills}) when skills != [], do:
query
|> where([p, s], s.name in ^skills)
|> group_by([p], p.id)
|> having([p, s], count(s.id) == ^length(skills))

defp maybe_filter_by_skills(query, _), do: query

def maybe_filter_by_city(query, %{city_search: city}) when city != "" do
query
|> where([p, s, l], ilike(l.city, ^"%#{city}%"))
end

def maybe_filter_by_city(query, _), do: query

def maybe_filter_by_country(query, %{country_search: country}) when country != "" do
query
|> where([p, s, l], ilike(l.country, ^"%#{country}%"))
end

def list_profiles_by_criteria(criteria) when is_list(criteria) do
query = from(p in Profile, where: not is_nil(p.name))
def maybe_filter_by_country(query, _), do: query


def list_profiles_by_criteria(criteria, filter_params \\ %{}) when is_list(criteria) do
query = Profile
|> where([p], not is_nil(p.name))
|> join(:left, [p], s in assoc(p, :skills))
|> join(:left, [p], l in assoc(p, :location))
|> join(:left, [p], u in assoc(p, :user))
|> where([p, ..., u], not is_nil(u.confirmed_at))
|> maybe_filter_by_country(filter_params)
|> maybe_filter_by_city(filter_params)
|> maybe_filter_by_skills(filter_params)
|> distinct([p], p.id)

Enum.reduce(criteria, query, fn
{:sort, %{sort_by: sort_by, sort_order: sort_order}}, query ->
Expand All @@ -52,6 +78,9 @@ defmodule MakerPassport.Maker do

{:preload, preload}, query ->
from q in query, preload: ^preload

{:current_id, current_id}, query ->
from q in query, where: q.id != ^current_id
end)
|> Repo.all()
|> Repo.preload([:user, :skills])
Expand Down Expand Up @@ -346,7 +375,15 @@ defmodule MakerPassport.Maker do
Repo.all(from l in Location, where: ilike(l.city, ^"%#{search_text}%"))
end

def to_city_list(cities, nil) do
def search_cities("", _), do: []

def search_cities(search_text, country) do
Repo.all(from l in Location, where: ilike(l.city, ^"%#{search_text}%") and l.country == ^country)
end

def to_city_list(cities, selected_city) when is_binary(selected_city) do
cities = Enum.filter(cities, &(&1.city != selected_city))

cities
|> Enum.map(&to_city_tuple/1)
|> Enum.sort_by(&elem(&1, 0))
Expand All @@ -364,6 +401,31 @@ defmodule MakerPassport.Maker do
{location.city, location.id}
end

def search_countries(""), do: []

def search_countries(search_text) do
Repo.all(from c in Location, where: ilike(c.country, ^"%#{search_text}%"), distinct: c.country)
end

def to_country_list(countries, selected_country) do
countries = Enum.filter(countries, &(&1.country != selected_country))

countries
|> Enum.map(&to_country_tuple/1)
|> Enum.sort_by(&elem(&1, 0))
end

defp to_country_tuple(location) do
{get_country_name(location.country), location.country}
end

def get_country_name(country_code) do
case Countries.get(country_code) do
nil -> "Unknown"
country -> country.name
end
end

@doc """
Gets a single website.

Expand Down
24 changes: 18 additions & 6 deletions lib/maker_passport_web/live/home_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,29 @@ defmodule MakerPassportWeb.HomeLive.Index do

@impl true
def mount(_params, _session, socket) do
latest_profiles =
Maker.list_profiles_by_criteria(
limit: 4,
sort: %{sort_by: :updated_at, sort_order: :desc},
preload: [:skills]
)
latest_profiles = fetch_latest_profiles(socket.assigns.current_user)

socket =
socket
|> assign(latest_profiles: latest_profiles)

{:ok, socket}
end

defp fetch_latest_profiles(nil) do
Maker.list_profiles_by_criteria(
limit: 4,
sort: %{sort_by: :updated_at, sort_order: :desc},
preload: [:skills]
)
end

defp fetch_latest_profiles(user) do
Maker.list_profiles_by_criteria(
limit: 4,
sort: %{sort_by: :updated_at, sort_order: :desc},
preload: [:skills],
current_id: user.id
)
end
end
132 changes: 104 additions & 28 deletions lib/maker_passport_web/live/profile_live/index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ defmodule MakerPassportWeb.ProfileLive.Index do
socket =
socket
|> assign(:page_title, "Maker Profiles")
|> assign(:search_skills, [])
|> assign(:filter_params, %{search_skills: [], country_search: "", city_search: ""})
|> assign(:no_skills_results, false)
|> assign(:profile, nil)
|> assign(:form, to_form(%{}, as: "filter_params"))
|> stream(:profiles, profiles)

{:ok, socket}
Expand All @@ -41,20 +42,33 @@ defmodule MakerPassportWeb.ProfileLive.Index do
end

@impl true
def handle_info({:typeahead, {name, _}, _}, socket) do
def handle_info(
{:typeahead, {country_name, country_code}, "country-search-picker" = id},
socket
) do
socket =
socket
|> update(:search_skills, fn skills -> [name | skills] end)
|> push_event(%{id: id, label: country_name})
|> update(:filter_params, fn params ->
Map.put(params, :country_search, country_code)
|> Map.put(:city_search, "")
end)

profiles =
case socket.assigns.current_user do
nil ->
Maker.list_profiles(socket.assigns.search_skills)
profiles = filter_profiles(socket.assigns.current_user, socket.assigns.filter_params)

user ->
Maker.list_profiles(socket.assigns.search_skills)
|> Enum.reject(fn profile -> profile.user && profile.user.id == user.id end)
end
{:noreply, stream(socket, :profiles, profiles)}
end

@impl true
def handle_info({:typeahead, {value, _}, id}, socket) do
socket =
socket
|> push_event(%{id: id, label: value})
|> update(:filter_params, fn params ->
add_filter_params(id, params, value)
end)

profiles = filter_profiles(socket.assigns.current_user, socket.assigns.filter_params)

socket =
socket
Expand All @@ -65,36 +79,98 @@ defmodule MakerPassportWeb.ProfileLive.Index do
end

@impl true
def handle_event("delete", %{"id" => id}, socket) do
profile = Maker.get_profile!(id)
{:ok, _} = Maker.delete_profile(profile)
def handle_event(
"filter-profiles",
%{"filter_params" => %{"country_search" => ""}},
%{assigns: %{filter_params: %{country_search: country_search}}} = socket
)
when country_search != "" do
handle_remove_search(socket, [:city_search, :country_search])
end

{:noreply, stream_delete(socket, :profiles, profile)}
@impl true
def handle_event(
"filter-profiles",
%{"filter_params" => %{"city_search" => ""}},
%{assigns: %{filter_params: %{city_search: city_search}}} = socket
) when city_search != "" do
handle_remove_search(socket, [:city_search])
end

@impl true
def handle_event("remove-skill", %{"skill_name" => skill_name}, socket) do
updated_skills =
Enum.filter(socket.assigns.search_skills, fn skill -> skill != skill_name end)

profiles =
case socket.assigns.current_user do
nil ->
Maker.list_profiles(updated_skills)

user ->
Maker.list_profiles(updated_skills)
|> Enum.reject(fn profile -> profile.user && profile.user.id == user.id end)
end
filter_params = socket.assigns.filter_params
updated_skills = Enum.filter(filter_params.search_skills, fn skill -> skill != skill_name end)
filter_params = %{filter_params | search_skills: updated_skills}
profiles = filter_profiles(socket.assigns.current_user, filter_params)

socket =
socket
|> assign(:no_skills_results, profiles == [])
|> assign(:search_skills, updated_skills)
|> assign(:filter_params, filter_params)
|> stream(:profiles, profiles)

{:noreply, socket}
end

@impl true
def handle_event("remove-country", _params, socket) do
handle_remove_search(socket, [:city_search, :country_search])
end

@impl true
def handle_event("remove-city", _params, socket) do
handle_remove_search(socket, [:city_search])
end

@impl true
def handle_event("delete", %{"id" => id}, socket) do
profile = Maker.get_profile!(id)
{:ok, _} = Maker.delete_profile(profile)

{:noreply, stream_delete(socket, :profiles, profile)}
end

@impl true
def handle_event(_event, _params, socket) do
{:noreply, socket}
end

defp add_filter_params("city-search-picker", filter_params, value) do
Map.put(filter_params, :city_search, value)
end

defp add_filter_params("skills-search-picker", filter_params, value) do
Map.update(filter_params, :search_skills, [value], fn skills -> [value | skills] end)
end

def filter_profiles(current_user, filter_params) do
case current_user do
nil ->
Maker.list_profiles_by_criteria([{:sort, %{sort_by: :updated_at, sort_order: :desc}}], filter_params)

user ->
Maker.list_profiles(filter_params)
|> Enum.reject(fn profile -> profile.user && profile.user.id == user.id end)
end
end

defp push_event(socket, %{id: id} = params) when id != "skills-search-picker" do
push_event(socket, "set-input-value", params)
end

defp push_event(socket, _), do: socket

defp handle_remove_search(socket, fields) do
filter_params =
Enum.reduce(fields, socket.assigns.filter_params, fn field, acc ->
Map.put(acc, field, "")
end)

profiles = filter_profiles(socket.assigns.current_user, filter_params)
{:noreply, stream(socket, :profiles, profiles) |> assign(:filter_params, filter_params)}
end

defp remove_selected_skill(skills, selected_skills) do
Enum.filter(skills, fn {skill, _} -> skill not in selected_skills end)
end
Expand Down
Loading