Refactor & fix friend search + address search
This commit is contained in:
parent
fd5bcbfc05
commit
b85e3cc96c
@ -21,8 +21,8 @@
|
|||||||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
||||||
import "phoenix_html"
|
import "phoenix_html"
|
||||||
// Establish Phoenix Socket and LiveView configuration.
|
// Establish Phoenix Socket and LiveView configuration.
|
||||||
import {Socket} from "phoenix"
|
import { Socket } from "phoenix"
|
||||||
import {LiveSocket} from "phoenix_live_view"
|
import { LiveSocket } from "phoenix_live_view"
|
||||||
import topbar from "../vendor/topbar"
|
import topbar from "../vendor/topbar"
|
||||||
import mapboxgl from "../vendor/mapbox-gl"
|
import mapboxgl from "../vendor/mapbox-gl"
|
||||||
|
|
||||||
@ -30,12 +30,12 @@ let Hooks = {};
|
|||||||
|
|
||||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||||||
let liveSocket = new LiveSocket("/live", Socket, {
|
let liveSocket = new LiveSocket("/live", Socket, {
|
||||||
hooks: Hooks,
|
hooks: Hooks,
|
||||||
params: { _csrf_token: csrfToken }
|
params: { _csrf_token: csrfToken }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Show progress bar on live navigation and form submits
|
// Show progress bar on live navigation and form submits
|
||||||
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
topbar.config({ barColors: { 0: "#29d" }, shadowColor: "rgba(0, 0, 0, .3)" })
|
||||||
window.addEventListener("phx:page-loading-start", info => topbar.show())
|
window.addEventListener("phx:page-loading-start", info => topbar.show())
|
||||||
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
|
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
|
||||||
|
|
||||||
@ -49,35 +49,58 @@ liveSocket.connect()
|
|||||||
window.liveSocket = liveSocket
|
window.liveSocket = liveSocket
|
||||||
|
|
||||||
window.hideElement = function (id) {
|
window.hideElement = function (id) {
|
||||||
var el = document.getElementById(id);
|
var el = document.getElementById(id);
|
||||||
el.hidden = true;
|
el.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.selectResult = function (latlon, display) {
|
window.selectMapResult = function (latlon, display) {
|
||||||
var name_el = document.querySelector("[autocomplete=name]");
|
var name_el = document.querySelector("[autocomplete=name]");
|
||||||
var latlon_el = document.querySelector("[autocomplete=latlon]");
|
var latlon_el = document.querySelector("[autocomplete=latlon]");
|
||||||
|
|
||||||
name_el.value = display;
|
name_el.value = display;
|
||||||
latlon_el.value = latlon;
|
latlon_el.value = latlon;
|
||||||
|
|
||||||
window.liveSocket.hooks.showMapbox.initMap();
|
window.liveSocket.hooks.showMapbox.initMap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.selectRelation = function (id, name) {
|
||||||
|
var e = new Event('eventSender');
|
||||||
|
e.data = {
|
||||||
|
id: id,
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
window.dispatchEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooks.NewRelation = {
|
||||||
|
mounted() {
|
||||||
|
var list_el = document.querySelector("div#relationships");
|
||||||
|
window.addEventListener("eventSender", e => {
|
||||||
|
this.pushEvent("select_relation", e.data, function (reply) {
|
||||||
|
console.log(reply);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Hooks.showMapbox = {
|
Hooks.showMapbox = {
|
||||||
initMap() {
|
initMap() {
|
||||||
mapboxgl.accessToken = 'pk.eyJ1IjoicnlhbnBhbmR5YSIsImEiOiJja3psM2tlcDA1MXl1Mm9uZmo5bGxpNzdxIn0.TwBKpTTypcD5fWFc8XRyHg';
|
mapboxgl.accessToken = 'pk.eyJ1IjoicnlhbnBhbmR5YSIsImEiOiJja3psM2tlcDA1MXl1Mm9uZmo5bGxpNzdxIn0.TwBKpTTypcD5fWFc8XRyHg';
|
||||||
|
|
||||||
const latlon = JSON.parse(document.querySelector("[autocomplete=latlon]").value).reverse();
|
const latlon = JSON.parse(document.querySelector("[autocomplete=latlon]").value).reverse();
|
||||||
|
|
||||||
const map = new mapboxgl.Map({
|
const map = new mapboxgl.Map({
|
||||||
container: 'map',
|
container: 'map',
|
||||||
style: 'mapbox://styles/mapbox/outdoors-v11',
|
style: 'mapbox://styles/mapbox/outdoors-v11',
|
||||||
center: latlon,
|
center: latlon,
|
||||||
zoom: 8
|
zoom: 8
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initMap();
|
if (this.el.getAttribute("latlon") != "null") {
|
||||||
}
|
this.initMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -23,14 +23,16 @@ defmodule Friends.Friend do
|
|||||||
:relationships,
|
:relationships,
|
||||||
Friends.Friend,
|
Friends.Friend,
|
||||||
join_through: Relationship,
|
join_through: Relationship,
|
||||||
join_keys: [friend_id: :id, relation_id: :id]
|
join_keys: [friend_id: :id, relation_id: :id],
|
||||||
|
on_delete: :delete_all
|
||||||
)
|
)
|
||||||
|
|
||||||
many_to_many(
|
many_to_many(
|
||||||
:reverse_relationships,
|
:reverse_relationships,
|
||||||
Friends.Friend,
|
Friends.Friend,
|
||||||
join_through: Relationship,
|
join_through: Relationship,
|
||||||
join_keys: [relation_id: :id, friend_id: :id]
|
join_keys: [relation_id: :id, friend_id: :id],
|
||||||
|
on_delete: :delete_all
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -46,10 +48,10 @@ defmodule Friends.Friend do
|
|||||||
:user_id,
|
:user_id,
|
||||||
:address_id
|
:address_id
|
||||||
])
|
])
|
||||||
|> Ecto.Changeset.validate_required([:name, :email, :phone, :born],
|
|> Ecto.Changeset.validate_required([:name],
|
||||||
message: "This field is required."
|
message: "This field is required."
|
||||||
)
|
)
|
||||||
|> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/, message: "Please enter your full name.")
|
# |> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/, message: "Please enter your full name.")
|
||||||
|> Ecto.Changeset.validate_format(
|
|> Ecto.Changeset.validate_format(
|
||||||
:email,
|
:email,
|
||||||
Regex.compile!("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"),
|
Regex.compile!("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"),
|
||||||
@ -88,7 +90,7 @@ defmodule Friends.Friend do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def new(params \\ %{}) do
|
def new(params \\ %{}) do
|
||||||
%Friend{id: :new}
|
%Friend{id: nil}
|
||||||
|> struct(params)
|
|> struct(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -120,13 +122,13 @@ defmodule Friends.Friend do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create(params \\ %{id: nil}) do
|
def create(params \\ %{id: nil}) do
|
||||||
%Friend{}
|
Friend.new()
|
||||||
|> Friend.changeset(%{params | id: nil})
|
|> Friend.changeset(params)
|
||||||
|> Map.put(:action, :insert)
|
|> Map.put(:action, :insert)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(params) do
|
def update(params) do
|
||||||
Friend.get_by_id(params.id |> String.to_integer())
|
Friend.get_by_id(params.id)
|
||||||
|> Friend.changeset(params)
|
|> Friend.changeset(params)
|
||||||
|> Map.put(:action, :update)
|
|> Map.put(:action, :update)
|
||||||
end
|
end
|
||||||
@ -156,12 +158,12 @@ defmodule Friends.Friend do
|
|||||||
|
|
||||||
def create_or_update(params) do
|
def create_or_update(params) do
|
||||||
case params.id do
|
case params.id do
|
||||||
"new" ->
|
nil ->
|
||||||
params
|
params
|
||||||
|> create()
|
|> create()
|
||||||
|> generate_slug()
|
|> generate_slug()
|
||||||
|> create_birth_event()
|
|
||||||
|> commit()
|
|> commit()
|
||||||
|
|> create_birth_event()
|
||||||
|
|
||||||
_number ->
|
_number ->
|
||||||
params
|
params
|
||||||
@ -189,7 +191,8 @@ defmodule Friends.Friend do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_be_edited_by(friend, user) do
|
def can_be_edited_by(friend, user) do
|
||||||
if user |> is_nil(), do: false, else: friend.id == user.profile.id
|
true
|
||||||
|
# if user |> is_nil(), do: false, else: friend.id == user.profile.id
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_address(%Friend{} = friend, address) do
|
def assign_address(%Friend{} = friend, address) do
|
||||||
@ -211,8 +214,10 @@ defmodule Friends.Friend do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def assign_user!(%Friend{} = friend) do
|
def assign_user!(%Friend{} = friend) do
|
||||||
%{friend: new_friend, user: _user} = assign_user(friend)
|
case assign_user(friend) do
|
||||||
new_friend
|
%{friend: new_friend, user: _user} -> new_friend
|
||||||
|
nil -> friend
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_user(%Friends.Friend{user: %Ecto.Association.NotLoaded{}} = model) do
|
def load_user(%Friends.Friend{user: %Ecto.Association.NotLoaded{}} = model) do
|
||||||
@ -260,7 +265,7 @@ defmodule Friends.Friend do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_birth_event(%Friend{id: id} = friend) do
|
def create_birth_event(%Friend{id: id} = friend) do
|
||||||
solo_relationship = Relationship.get_or_new(friend, friend)
|
solo_relationship = Relationship.get_or_create(friend, friend)
|
||||||
|
|
||||||
Event.get_or_create(%{
|
Event.get_or_create(%{
|
||||||
name: "Born",
|
name: "Born",
|
||||||
@ -270,4 +275,14 @@ defmodule Friends.Friend do
|
|||||||
relationship_id: solo_relationship.id
|
relationship_id: solo_relationship.id
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_relationship(friend1, friend2) do
|
||||||
|
rel = Relationship.get_or_create(friend1, friend2)
|
||||||
|
rel |> Relationship.members()
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_relationship(friend1, friend2) do
|
||||||
|
Relationship.delete(friend1, friend2)
|
||||||
|
friend1.id |> Friend.get_by_id()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
44
friends/lib/friends/friend/search.ex
Normal file
44
friends/lib/friends/friend/search.ex
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
defmodule Friends.Friend.Search do
|
||||||
|
alias Friends.Friend
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
@repo Friends.Repo
|
||||||
|
|
||||||
|
def autocomplete(str, friend) do
|
||||||
|
@repo.all(
|
||||||
|
from(
|
||||||
|
f in Friend,
|
||||||
|
where: ilike(f.name, ^"%#{str}%")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> remove_self(friend)
|
||||||
|
|> remove_existing(friend)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_self(list, friend) do
|
||||||
|
list
|
||||||
|
|> Enum.filter(fn result ->
|
||||||
|
result.id != friend.id
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_existing(list, friend) do
|
||||||
|
existing =
|
||||||
|
friend
|
||||||
|
|> Helpers.relations()
|
||||||
|
|> Enum.map(& &1.id)
|
||||||
|
|
||||||
|
list
|
||||||
|
|> Enum.filter(fn result ->
|
||||||
|
result.id not in existing
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_result(friend) do
|
||||||
|
%{
|
||||||
|
id: "friend-#{friend.id}",
|
||||||
|
value: friend.id,
|
||||||
|
name: friend.name
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,4 +1,7 @@
|
|||||||
defmodule Friends.Places.Search do
|
defmodule Friends.Places.Search do
|
||||||
|
import Ecto.Query
|
||||||
|
@repo Friends.Repo
|
||||||
|
|
||||||
def api_key,
|
def api_key,
|
||||||
do:
|
do:
|
||||||
"pk.eyJ1IjoicnlhbnBhbmR5YSIsImEiOiJja3psM2tlcDA1MXl1Mm9uZmo5bGxpNzdxIn0.TwBKpTTypcD5fWFc8XRyHg"
|
"pk.eyJ1IjoicnlhbnBhbmR5YSIsImEiOiJja3psM2tlcDA1MXl1Mm9uZmo5bGxpNzdxIn0.TwBKpTTypcD5fWFc8XRyHg"
|
||||||
@ -14,6 +17,22 @@ defmodule Friends.Places.Search do
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def known_places(str) do
|
||||||
|
@repo.all(
|
||||||
|
from(
|
||||||
|
p in Friends.Places.Place,
|
||||||
|
where: ilike(p.name, ^"%#{str}%")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Enum.map(
|
||||||
|
&%{
|
||||||
|
"center" => &1.latlon,
|
||||||
|
"place_name" => &1.name,
|
||||||
|
"id" => "known.#{&1.id}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def autocomplete(str, region \\ nil) do
|
def autocomplete(str, region \\ nil) do
|
||||||
viewbox =
|
viewbox =
|
||||||
if region do
|
if region do
|
||||||
@ -25,8 +44,9 @@ defmodule Friends.Places.Search do
|
|||||||
|
|
||||||
response = HTTPoison.get!(url)
|
response = HTTPoison.get!(url)
|
||||||
results = Poison.decode!(response.body)
|
results = Poison.decode!(response.body)
|
||||||
|
IO.inspect(results["features"])
|
||||||
|
|
||||||
results["features"]
|
known_places(str) ++ results["features"]
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_features(%{
|
def parse_features(%{
|
||||||
@ -36,7 +56,7 @@ defmodule Friends.Places.Search do
|
|||||||
}) do
|
}) do
|
||||||
%{
|
%{
|
||||||
name: name,
|
name: name,
|
||||||
latlon: lonlat |> Enum.reverse() |> Poison.encode!(),
|
value: lonlat |> Enum.reverse() |> Poison.encode!(),
|
||||||
id: id |> String.replace(".", "-")
|
id: id |> String.replace(".", "-")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@ -82,7 +82,7 @@ defmodule Friends.Relationship do
|
|||||||
rel_type = if id1 == id2, do: 0, else: type
|
rel_type = if id1 == id2, do: 0, else: type
|
||||||
|
|
||||||
relationship =
|
relationship =
|
||||||
@repo.insert(%Relationship{
|
@repo.insert!(%Relationship{
|
||||||
friend_id: id1,
|
friend_id: id1,
|
||||||
relation_id: id2,
|
relation_id: id2,
|
||||||
type: rel_type
|
type: rel_type
|
||||||
@ -122,6 +122,10 @@ defmodule Friends.Relationship do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete(a, b) do
|
||||||
|
get(a, b) |> Friends.Repo.delete!()
|
||||||
|
end
|
||||||
|
|
||||||
def get_by_slugs([slug1, slug2]) do
|
def get_by_slugs([slug1, slug2]) do
|
||||||
friend1 = slug1 |> Friend.get_by_slug()
|
friend1 = slug1 |> Friend.get_by_slug()
|
||||||
friend2 = slug2 |> Friend.get_by_slug()
|
friend2 = slug2 |> Friend.get_by_slug()
|
||||||
|
|||||||
@ -4,19 +4,29 @@ defmodule FriendsWeb.Components.Autocomplete do
|
|||||||
|
|
||||||
alias Phoenix.LiveView.JS
|
alias Phoenix.LiveView.JS
|
||||||
|
|
||||||
|
def bolden(string, substring) do
|
||||||
|
String.replace(
|
||||||
|
string,
|
||||||
|
substring,
|
||||||
|
"<b>#{substring}</b>"
|
||||||
|
)
|
||||||
|
|> Phoenix.HTML.raw()
|
||||||
|
end
|
||||||
|
|
||||||
def search_results(assigns) do
|
def search_results(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div id="search-results" class="absolute w-full md:w-max bottom-16">
|
<div id="search-results" class="absolute w-full bottom-16 left-0">
|
||||||
|
|
||||||
<%= if @search_results do %>
|
<%= if @search_results do %>
|
||||||
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-2/3 overflow-auto">
|
<ul tabindex="0" class="dropdown-content menu p-0 m-0 shadow bg-base-200 rounded-box overflow-auto">
|
||||||
<%= for r <- @search_results do %>
|
<%= for r <- @search_results do %>
|
||||||
<li class="py-0 my-0">
|
<li class="p-0 m-0">
|
||||||
<.link id={r[:id]}
|
<.link id={r[:id]}
|
||||||
latlon={r[:latlon]}
|
phx_value={r[:value]}
|
||||||
class="search_result"
|
class="search_result"
|
||||||
onMouseDown={"selectResult('#{r[:latlon]}', '#{r[:name]}')"}>
|
phx-hook="NewRelation"
|
||||||
<%= r[:name] %>
|
onMouseDown={"#{@select_fxn}('#{r[:value]}', '#{r[:name]}')"}>
|
||||||
|
<%= r[:name] |> bolden(@search_query) %>
|
||||||
</.link>
|
</.link>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -24,29 +34,6 @@ defmodule FriendsWeb.Components.Autocomplete do
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script language="javascript">
|
|
||||||
window.elSearch_results = document.querySelector("#search-results");
|
|
||||||
window.elSearchInput = document.querySelector("input[autocomplete=value]");
|
|
||||||
window.elSearchResultID = document.querySelector("input[autocomplete=id]");
|
|
||||||
|
|
||||||
window.hideResults = function () {
|
|
||||||
window.elSearch_results.classList.add("hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.showResults = function () {
|
|
||||||
document.getElementById("search-results").classList.remove("hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.selectSearchResult = function (result) {
|
|
||||||
const elSelectedResult = document.querySelector("#" + result);
|
|
||||||
const searchResultID = elSelectedResult.attributes.id.value;
|
|
||||||
const display = elSelectedResult.innerText;
|
|
||||||
|
|
||||||
window.elSearchInput.value = display;
|
|
||||||
window.elSearchResultID.value = searchResultID;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
25
friends/lib/friends_web/live/components/cards.ex
Normal file
25
friends/lib/friends_web/live/components/cards.ex
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
defmodule FriendsWeb.Components.Cards do
|
||||||
|
use FriendsWeb, :live_component
|
||||||
|
|
||||||
|
def relationship_card(assigns) do
|
||||||
|
~H"""
|
||||||
|
<div id={"relation-#{@relation.id}"} class="card card-compact w-96 bg-base-100 shadow-xl">
|
||||||
|
<figure><img src="https://placeimg.com/400/225/people" alt={@relation.id} /></figure>
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 class="card-title">
|
||||||
|
<.link navigate={Routes.friends_show_path(FriendsWeb.Endpoint, :overview, @relation.slug)} class=""><%=@relation.name%></.link>
|
||||||
|
<%= if @relationship |> Friends.Relationship.get_relation do %>
|
||||||
|
<div class={"badge badge-#{@relationship |> Friends.Relationship.get_color}"}><%= @relationship |> Friends.Relationship.get_relation %></div>
|
||||||
|
<% end %>
|
||||||
|
</h3>
|
||||||
|
<p>If a dog chews shoes whose shoes does he choose?</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= if @editable do %>
|
||||||
|
Delete
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -4,7 +4,7 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
import Helpers
|
import Helpers
|
||||||
import FriendsWeb.LiveHelpers
|
import FriendsWeb.LiveHelpers
|
||||||
alias Friends.Friend
|
alias Friends.Friend
|
||||||
alias FriendsWeb.Components.{Autocomplete, Map}
|
alias FriendsWeb.Components.{Autocomplete, Map, Cards}
|
||||||
alias Phoenix.LiveView.JS
|
alias Phoenix.LiveView.JS
|
||||||
|
|
||||||
def header(assigns) do
|
def header(assigns) do
|
||||||
@ -48,7 +48,9 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
|
|
||||||
def show_page(:overview, assigns) do
|
def show_page(:overview, assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<Map.show address_latlon={@latlon} />
|
<%= if @address_latlon != "null" do %>
|
||||||
|
<Map.show address_latlon={@address_latlon} />
|
||||||
|
<% end %>
|
||||||
<ul class="py-4 pl-0 md:text-xl h-1/2">
|
<ul class="py-4 pl-0 md:text-xl h-1/2">
|
||||||
<li class="flex flex-row mb-8 gap-6">
|
<li class="flex flex-row mb-8 gap-6">
|
||||||
<strong class="w-28 text-right">Nickname:</strong>
|
<strong class="w-28 text-right">Nickname:</strong>
|
||||||
@ -77,8 +79,12 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
</li>
|
</li>
|
||||||
<li class="flex flex-row mb-8 gap-6">
|
<li class="flex flex-row mb-8 gap-6">
|
||||||
<strong class="w-28 text-right">Address:</strong>
|
<strong class="w-28 text-right">Address:</strong>
|
||||||
|
<%= if @address_latlon == "null" do %>
|
||||||
|
<span class="italic">none</span>
|
||||||
|
<% else %>
|
||||||
<div class=""><%= @address %></div>
|
<div class=""><%= @address %></div>
|
||||||
<input type="hidden" autocomplete="latlon" value={@latlon}/>
|
<% end %>
|
||||||
|
<input type="hidden" autocomplete="latlon" value={@address_latlon}/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
"""
|
"""
|
||||||
@ -105,22 +111,11 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
def show_page(:relationships, assigns) do
|
def show_page(:relationships, assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div id="relationships" class="flex md:flex-row flex-col gap-8 p-8">
|
<div id="relationships" class="flex md:flex-row flex-col gap-8 p-8">
|
||||||
<%= for relation <- @friend |> relations do %>
|
<%= for relation <- @relationships do %>
|
||||||
<% relationship = relation(@friend, relation) %>
|
<% relationship = relation(@friend, relation) %>
|
||||||
<div id={"relation-#{relation.id}"} class="card card-compact w-96 bg-base-100 shadow-xl">
|
<Cards.relationship_card relation={relation} relationship={relationship} editable={@editable}/>
|
||||||
<figure><img src="https://placeimg.com/400/225/people" alt={relation.id} /></figure>
|
|
||||||
<div class="card-body">
|
|
||||||
<h3 class="card-title">
|
|
||||||
<%= relation.name %>
|
|
||||||
<%= if relationship |> Friends.Relationship.get_relation do %>
|
|
||||||
<div class={"badge badge-#{relationship |> Friends.Relationship.get_color}"}><%= relationship |> Friends.Relationship.get_relation %></div>
|
|
||||||
<% end %>
|
|
||||||
</h3>
|
|
||||||
<p>If a dog chews shoes whose shoes does he choose?</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= if @friend |> relations |> Enum.empty? do %>
|
<%= if @relationships |> Enum.empty? do %>
|
||||||
<div class="italic">No relationships on record yet.</div>
|
<div class="italic">No relationships on record yet.</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@ -160,14 +155,12 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
<div class="min-w-fit flex place-items-center mx-4"><%= error_tag f, :name %></div>
|
<div class="min-w-fit flex place-items-center mx-4"><%= error_tag f, :name %></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= if @address_latlon do %>
|
<Map.show address_latlon={@address_latlon} />
|
||||||
<Map.show address_latlon={@address_latlon} />
|
|
||||||
<% end %>
|
|
||||||
<ul class="py-4 pl-0 h-1/2">
|
<ul class="py-4 pl-0 h-1/2">
|
||||||
<li class="flex flex-row gap-x-6">
|
<li class="flex flex-row gap-x-6">
|
||||||
<strong class="md:text-xl w-20 md:w-28 shrink-0 text-right">Email:</strong>
|
<strong class="md:text-xl w-20 md:w-28 shrink-0 text-right">Email:</strong>
|
||||||
<div class="flex flex-col h-16">
|
<div class="flex flex-col h-16">
|
||||||
<%= text_input f, :email, class: "input input-primary input-sm md:input-md input-disabled", phx_debounce: "blur", value: @current_user.email %>
|
<%= text_input f, :email, class: "input input-primary input-sm md:input-md input-disabled", phx_debounce: "blur", value: @friend.email %>
|
||||||
<div class="min-w-fit flex place-items-center mr-4"><%= error_tag f, :email %></div>
|
<div class="min-w-fit flex place-items-center mr-4"><%= error_tag f, :email %></div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@ -194,7 +187,7 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
<li class="flex flex-row gap-x-6 relative">
|
<li class="flex flex-row gap-x-6 relative">
|
||||||
<strong class="md:text-xl w-20 md:w-28 shrink-0 text-right">Address:</strong>
|
<strong class="md:text-xl w-20 md:w-28 shrink-0 text-right">Address:</strong>
|
||||||
<div class="flex flex-col h-16">
|
<div class="flex flex-col h-16">
|
||||||
<%= text_input f, :address_query, value: @address_query,
|
<%= text_input f, :search_query, value: @search_query,
|
||||||
class: "input input-primary input-sm md:input-md",
|
class: "input input-primary input-sm md:input-md",
|
||||||
phx_debounce: "500",
|
phx_debounce: "500",
|
||||||
phx_change: :address_search,
|
phx_change: :address_search,
|
||||||
@ -206,7 +199,11 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
phx_change: "validate"
|
phx_change: "validate"
|
||||||
%>
|
%>
|
||||||
</div>
|
</div>
|
||||||
<Autocomplete.search_results search_results={@search_results}/>
|
<Autocomplete.search_results
|
||||||
|
search_results={@search_results}
|
||||||
|
search_query={@search_query}
|
||||||
|
select_fxn="selectMapResult"
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="form-control flex flex-row gap-x-4 md:justify-end mb-4 md:w-1/2">
|
<div class="form-control flex flex-row gap-x-4 md:justify-end mb-4 md:w-1/2">
|
||||||
@ -234,6 +231,41 @@ defmodule FriendsWeb.FriendsLive.Components do
|
|||||||
|
|
||||||
def edit_page(:relationships, assigns) do
|
def edit_page(:relationships, assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
|
<%= show_page(:relationships, assigns) %>
|
||||||
|
|
||||||
|
<.form for={@changeset} let={f}>
|
||||||
|
<ul class="py-4 pl-0 h-1/2">
|
||||||
|
<li class="flex flex-row gap-x-6 relative">
|
||||||
|
<strong class="md:text-xl basis-auto shrink-0 text-right">Type a name:</strong>
|
||||||
|
<div class="flex flex-col h-16 relative">
|
||||||
|
<%= text_input f, :search_query, value: @search_query,
|
||||||
|
class: "input input-primary input-sm md:input-md",
|
||||||
|
phx_debounce: "500",
|
||||||
|
phx_change: :relation_search,
|
||||||
|
phx_click: JS.show(to: "#search-results"),
|
||||||
|
phx_blur: JS.hide(to: "#search-results"),
|
||||||
|
autocomplete: "name" %>
|
||||||
|
<%= hidden_input f, :relation_id, value: @relation_id,
|
||||||
|
id: "relation-id", autocomplete: "relation-id",
|
||||||
|
phx_change: "validate"
|
||||||
|
%>
|
||||||
|
<Autocomplete.search_results
|
||||||
|
search_results={@search_results}
|
||||||
|
search_query={@search_query}
|
||||||
|
select_fxn="selectRelation"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="form-control flex flex-row gap-x-4 md:justify-end mb-4 md:w-1/2">
|
||||||
|
<%= if @live_action != :welcome do %>
|
||||||
|
<div class="flex-1">
|
||||||
|
<.link patch={Routes.friends_show_path(FriendsWeb.Endpoint, :overview, @friend.slug)} class="btn btn-block btn-outline">back</.link>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</.form>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,7 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
live_action = socket.assigns.live_action || :overview
|
live_action = socket.assigns.live_action || :overview
|
||||||
|
|
||||||
friend = Friend.get_by_slug(slug)
|
friend = Friend.get_by_slug(slug)
|
||||||
|
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
||||||
|
|
||||||
if(live_action) do
|
if(live_action) do
|
||||||
{:ok,
|
{:ok,
|
||||||
@ -35,18 +36,24 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
|> assign(:live_action, live_action)
|
|> assign(:live_action, live_action)
|
||||||
|> assign_current_user(token |> Map.get("user_token"))
|
|> assign_current_user(token |> Map.get("user_token"))
|
||||||
|> assign(:friend, friend)
|
|> assign(:friend, friend)
|
||||||
|
|> assign(:editable, editable)
|
||||||
|> assign(:action, Routes.friends_path(socket, :update))
|
|> assign(:action, Routes.friends_path(socket, :update))
|
||||||
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
||||||
|> assign(:changeset, %Friend{} |> Friend.changeset())
|
|> assign(:changeset, %Friend{} |> Friend.changeset())
|
||||||
|> assign(:address_query, nil)
|
|> assign(:search_query, nil)
|
||||||
|> assign(:search_results, nil)}
|
|> assign(:search_results, nil)}
|
||||||
else
|
else
|
||||||
{:ok, socket |> redirect(to: Routes.friends_show_path(socket, :overview, friend.slug))}
|
{:ok, socket |> redirect(to: Routes.friends_show_path(socket, :overview, friend.slug))}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_params(%{"slug" => slug} = _attrs, _url, socket) do
|
# Overview form page
|
||||||
live_action = socket.assigns.live_action || false
|
def handle_params(
|
||||||
|
%{"slug" => slug} = _attrs,
|
||||||
|
_url,
|
||||||
|
%{assigns: %{live_action: :overview}} = socket
|
||||||
|
) do
|
||||||
|
live_action = socket.assigns.live_action
|
||||||
|
|
||||||
friend = Friend.get_by_slug(slug)
|
friend = Friend.get_by_slug(slug)
|
||||||
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
||||||
@ -58,13 +65,57 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
|> assign_friend(friend)
|
|> assign_friend(friend)
|
||||||
|> assign(:action, Routes.friends_path(socket, :update))
|
|> assign(:action, Routes.friends_path(socket, :update))
|
||||||
|> assign(:live_action, live_action)
|
|> assign(:live_action, live_action)
|
||||||
|> assign(:address_query, address_query)
|
|> assign(:search_query, address_query)
|
||||||
|> assign(:address_latlon, address_latlon |> Poison.encode!())
|
|> assign(:address_latlon, address_latlon |> Poison.encode!())
|
||||||
|> assign(:search_results, nil)
|
|> assign(:search_results, nil)
|
||||||
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
||||||
|> assign(:editable, editable)}
|
|> assign(:editable, editable)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add a relationship
|
||||||
|
def handle_params(
|
||||||
|
%{"slug" => slug} = _attrs,
|
||||||
|
_url,
|
||||||
|
%{assigns: %{live_action: :relationships}} = socket
|
||||||
|
) do
|
||||||
|
live_action = socket.assigns.live_action
|
||||||
|
|
||||||
|
friend = Friend.get_by_slug(slug)
|
||||||
|
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign_friend(friend)
|
||||||
|
|> assign(:relationships, friend |> relations)
|
||||||
|
|> assign(:live_action, live_action)
|
||||||
|
|> assign(:search_query, nil)
|
||||||
|
|> assign(:relation_id, nil)
|
||||||
|
|> assign(:search_results, nil)
|
||||||
|
|> assign(:editable, editable)
|
||||||
|
|> title(friend.name <> " - " <> (live_action |> titlecase))}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Timeline edit
|
||||||
|
def handle_params(
|
||||||
|
%{"slug" => slug} = _attrs,
|
||||||
|
_url,
|
||||||
|
%{assigns: %{live_action: :timeline}} = socket
|
||||||
|
) do
|
||||||
|
live_action = socket.assigns.live_action
|
||||||
|
|
||||||
|
friend = Friend.get_by_slug(slug)
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign_friend(friend)
|
||||||
|
|> assign(:live_action, live_action)
|
||||||
|
|> assign(:search_query, nil)
|
||||||
|
|> assign(:relation_id, nil)
|
||||||
|
|> assign(:search_results, nil)
|
||||||
|
|> title(friend.name <> " - " <> (live_action |> titlecase))}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Catch-all (aka, new friend form)
|
||||||
def handle_params(_attrs, _token, socket) do
|
def handle_params(_attrs, _token, socket) do
|
||||||
friend = Friend.new()
|
friend = Friend.new()
|
||||||
|
|
||||||
@ -77,7 +128,12 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
|> title("Welcome")}
|
|> title("Welcome")}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("validate", %{"friend" => form_params}, %{assigns: %{friend: friend}} = socket) do
|
# Validate overview form page
|
||||||
|
def handle_event(
|
||||||
|
"validate",
|
||||||
|
%{"friend" => form_params},
|
||||||
|
%{assigns: %{friend: friend, live_action: :overview}} = socket
|
||||||
|
) do
|
||||||
id = form_params["id"] |> parse_id
|
id = form_params["id"] |> parse_id
|
||||||
name = form_params["name"]
|
name = form_params["name"]
|
||||||
nickname = form_params["nickname"]
|
nickname = form_params["nickname"]
|
||||||
@ -126,7 +182,7 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
slug = form_params["slug"] || name |> to_slug
|
slug = form_params["slug"] || name |> to_slug
|
||||||
id = form_params["id"] || :new
|
id = form_params["id"] || :new
|
||||||
address_latlon = form_params["address_latlon"] |> Poison.decode!()
|
address_latlon = form_params["address_latlon"] |> Poison.decode!()
|
||||||
address_query = form_params["address_query"]
|
address_query = form_params["search_query"]
|
||||||
|
|
||||||
address = %Friends.Places.Place{
|
address = %Friends.Places.Place{
|
||||||
name: address_query,
|
name: address_query,
|
||||||
@ -134,8 +190,14 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
}
|
}
|
||||||
|
|
||||||
new_address =
|
new_address =
|
||||||
address
|
case address.latlon do
|
||||||
|> Friends.Places.Place.get_or_create()
|
nil ->
|
||||||
|
%{id: nil}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
address
|
||||||
|
|> Friends.Places.Place.get_or_create()
|
||||||
|
end
|
||||||
|
|
||||||
new_params = %{
|
new_params = %{
|
||||||
id: id,
|
id: id,
|
||||||
@ -161,17 +223,61 @@ defmodule FriendsWeb.FriendsLive.Edit do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("address_search", %{"friend" => %{"address_query" => query}}, socket) do
|
def handle_event("address_search", %{"friend" => %{"search_query" => query}}, socket) do
|
||||||
results = Places.Search.autocomplete(query) |> Enum.map(&Places.Search.parse_features/1)
|
results = Places.Search.autocomplete(query) |> Enum.map(&Places.Search.parse_features/1)
|
||||||
|
|
||||||
if query == "" do
|
if query == "" do
|
||||||
{:noreply, socket |> assign(:search_results, nil)}
|
{:noreply, socket |> assign(:search_results, nil)}
|
||||||
else
|
else
|
||||||
{:noreply, socket |> assign(:search_results, results)}
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign(:search_results, results)
|
||||||
|
|> assign(:search_query, query)
|
||||||
|
|> assign(:select_fxn, "selectMapResult")}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event(event, unsigned_params, socket) do
|
def handle_event(
|
||||||
|
"relation_search",
|
||||||
|
%{"friend" => %{"search_query" => query}},
|
||||||
|
%{assigns: %{friend: friend}} = socket
|
||||||
|
) do
|
||||||
|
if query == "" do
|
||||||
|
{:noreply, socket |> assign(:search_results, nil)}
|
||||||
|
else
|
||||||
|
results =
|
||||||
|
(Friend.Search.autocomplete(query, friend) ++
|
||||||
|
[Friend.new(%{name: query})])
|
||||||
|
|> Enum.map(&Friend.Search.parse_result/1)
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign(:search_results, results)
|
||||||
|
|> assign(:search_query, query)
|
||||||
|
|> assign(:select_fxn, "selectRelation")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event(
|
||||||
|
"select_relation",
|
||||||
|
%{"id" => rel_id, "name" => rel_name},
|
||||||
|
%{assigns: %{friend: friend, relationships: relationships}} = socket
|
||||||
|
) do
|
||||||
|
new_rel =
|
||||||
|
case rel_id do
|
||||||
|
"new" -> Friend.create(%{name: rel_name})
|
||||||
|
_num -> Friend.get_by_id(rel_id |> String.to_integer())
|
||||||
|
end
|
||||||
|
|
||||||
|
[updated_friend, updated_relation] = friend |> Friend.create_relationship(new_rel)
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign_friend(updated_friend)
|
||||||
|
|> assign(:relationships, updated_friend |> relations)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event(_event, _unsigned_params, socket) do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -19,7 +19,7 @@ defmodule FriendsWeb.FriendsLive.Show do
|
|||||||
|> assign_current_user(token |> Map.get("user_token"))
|
|> assign_current_user(token |> Map.get("user_token"))
|
||||||
|> assign(:friend, friend)
|
|> assign(:friend, friend)
|
||||||
|> assign(:address, address)
|
|> assign(:address, address)
|
||||||
|> assign(:latlon, latlon |> Poison.encode!())
|
|> assign(:address_latlon, latlon |> Poison.encode!())
|
||||||
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
||||||
|> assign(:changeset, %Friend{} |> Friend.changeset())
|
|> assign(:changeset, %Friend{} |> Friend.changeset())
|
||||||
|> assign(:editable, editable)
|
|> assign(:editable, editable)
|
||||||
@ -29,16 +29,35 @@ defmodule FriendsWeb.FriendsLive.Show do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_params(%{"slug" => slug} = attrs, _url, socket) do
|
def handle_params(
|
||||||
live_action = socket.assigns.live_action || false
|
%{"slug" => slug} = attrs,
|
||||||
|
_url,
|
||||||
|
%{assigns: %{friend: friend, live_action: :overview}} = socket
|
||||||
|
) do
|
||||||
friend = Friend.get_by_slug(slug)
|
friend = Friend.get_by_slug(slug)
|
||||||
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
||||||
|
live_action = socket.assigns.live_action
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> assign_friend(friend)
|
|> assign_friend(friend)
|
||||||
|> assign(:live_action, live_action)
|
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
||||||
|
|> assign(:editable, editable)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_params(
|
||||||
|
%{"slug" => slug} = attrs,
|
||||||
|
_url,
|
||||||
|
%{assigns: %{friend: friend, live_action: :relationships}} = socket
|
||||||
|
) do
|
||||||
|
friend = Friend.get_by_slug(slug)
|
||||||
|
editable = friend |> Friend.can_be_edited_by(socket.assigns[:current_user])
|
||||||
|
live_action = socket.assigns.live_action
|
||||||
|
|
||||||
|
{:noreply,
|
||||||
|
socket
|
||||||
|
|> assign_friend(friend)
|
||||||
|
|> assign(:relationships, friend |> Helpers.relations())
|
||||||
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
|> title(friend.name <> " - " <> (live_action |> titlecase))
|
||||||
|> assign(:editable, editable)}
|
|> assign(:editable, editable)}
|
||||||
end
|
end
|
||||||
|
|||||||
@ -84,7 +84,6 @@ defmodule FriendsWeb.LiveHelpers do
|
|||||||
socket
|
socket
|
||||||
|> assign(:friend, friend)
|
|> assign(:friend, friend)
|
||||||
|> assign(:changeset, friend |> Friend.changeset())
|
|> assign(:changeset, friend |> Friend.changeset())
|
||||||
|> assign(:relationships, friend.relationships)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Same thing, but this time we have a changeset we want to keep
|
# Same thing, but this time we have a changeset we want to keep
|
||||||
@ -92,6 +91,5 @@ defmodule FriendsWeb.LiveHelpers do
|
|||||||
socket
|
socket
|
||||||
|> assign(:friend, friend)
|
|> assign(:friend, friend)
|
||||||
|> assign(:changeset, changeset)
|
|> assign(:changeset, changeset)
|
||||||
|> assign(:relationships, friend.relationships)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -90,4 +90,32 @@ defmodule Helpers do
|
|||||||
def events(%Friends.Relationship{} = relationship) do
|
def events(%Friends.Relationship{} = relationship) do
|
||||||
relationship.events
|
relationship.events
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def random_string(length) do
|
||||||
|
:crypto.strong_rand_bytes(length)
|
||||||
|
|> Base.url_encode64()
|
||||||
|
|> binary_part(0, length)
|
||||||
|
|> String.replace(~r/-/, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def unique_friend_email, do: "user#{System.unique_integer()}@example.com"
|
||||||
|
def valid_friend_name, do: "#{random_string(5)} Mc#{random_string(5)}"
|
||||||
|
def valid_friend_phone, do: "+1 (917) 624 2939" |> Helpers.format_phone()
|
||||||
|
def valid_friend_birthdate, do: ~D"1990-05-05"
|
||||||
|
|
||||||
|
def valid_friend_attributes(attrs \\ %{}) do
|
||||||
|
Enum.into(attrs, %{
|
||||||
|
id: nil,
|
||||||
|
name: valid_friend_name(),
|
||||||
|
phone: valid_friend_phone(),
|
||||||
|
born: valid_friend_birthdate(),
|
||||||
|
email: unique_friend_email()
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def friend_fixture(attrs \\ %{}) do
|
||||||
|
attrs
|
||||||
|
|> valid_friend_attributes()
|
||||||
|
|> Friends.Friend.create_or_update()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user