Compare commits

...

2 Commits

Author SHA1 Message Date
Ryan Pandya
606b0b0d26 Add CSS, deps (tailwind, daisyui, timex, yamerl) 2022-10-21 13:31:27 -07:00
Ryan Pandya
7b2a5f2baa Add migrations, helpers, dev config, models 2022-10-21 13:01:58 -07:00
26 changed files with 2233 additions and 109 deletions

3
friends/.gitignore vendored
View File

@ -1,6 +1,9 @@
# The directory Mix will write compiled artifacts to.
/_build/
# My old umbrella code, just for reference / copying things over.
/_old/
# If you run "mix test --cover", coverage assets end up here.
/cover/

View File

@ -1,5 +1,16 @@
/* This file is for your main application CSS */
@import "./phoenix.css";
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
/* Override some defaults I don't like */
.input{
border-radius: inherit !important;
}
.prose{
max-width: inherit !important;
}
/* Alerts and form errors used by phx.new */
.alert {

File diff suppressed because one or more lines are too long

1586
friends/assets/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
{
"dependencies": {
"daisyui": "^2.27.0",
"phoenix": "file:../../../deps/phoenix",
"phoenix_html": "file:../../../deps/phoenix_html",
"phoenix_live_view": "file:../../../deps/phoenix_live_view"
}
}

View File

@ -0,0 +1,24 @@
// See the Tailwind configuration guide for advanced usage
// https://tailwindcss.com/docs/configuration
let plugin = require('tailwindcss/plugin')
module.exports = {
content: [
'./js/**/*.js',
'../lib/*_web.ex',
'../lib/*_web/**/*.*ex'
],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
require("@tailwindcss/typography"),
require("daisyui"),
plugin(({addVariant}) => addVariant('phx-no-feedback', ['&.phx-no-feedback', '.phx-no-feedback &'])),
plugin(({addVariant}) => addVariant('phx-click-loading', ['&.phx-click-loading', '.phx-click-loading &'])),
plugin(({addVariant}) => addVariant('phx-submit-loading', ['&.phx-submit-loading', '.phx-submit-loading &'])),
plugin(({addVariant}) => addVariant('phx-change-loading', ['&.phx-change-loading', '.phx-change-loading &']))
]
}

View File

@ -0,0 +1,74 @@
import Config
# For development, we disable any cache and enable
# debugging and code reloading.
#
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with esbuild to bundle .js and .css sources.
config :friends_web, FriendsWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
check_origin: false,
code_reloader: true,
debug_errors: true,
secret_key_base: "qT0o4eu1vjzJ+v51AGuduGXxBqskKT7e8C7z/Kk34odJQjzKW5mGtB8p4XF+piVO",
watchers: [
# Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]}
]
# ## SSL Support
#
# In order to use HTTPS in development, a self-signed
# certificate can be generated by running the following
# Mix task:
#
# mix phx.gen.cert
#
# Note that this task requires Erlang/OTP 20 or later.
# Run `mix help phx.gen.cert` for more information.
#
# The `http:` config above can be replaced with:
#
# https: [
# port: 4001,
# cipher_suite: :strong,
# keyfile: "priv/cert/selfsigned_key.pem",
# certfile: "priv/cert/selfsigned.pem"
# ],
#
# If desired, both `http:` and `https:` keys can be
# configured to run both http and https servers on
# different ports.
# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"
# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
# Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive.
config :phoenix, :stacktrace_depth, 20
config :friends_web, FriendsWeb.Endpoint,
live_reload: [
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
~r"priv/gettext/.*(po)$",
~r"lib/friends_web/(live|views)/.*(ex)$",
~r"lib/friends_web/templates/.*(eex)$",
]
]
config :friends, dir: "db_dev"
config :friends, Friends.Repo,
database: "friends_crm",
username: "postgres",
password: "pleasework",
hostname: "10.0.0.22",
port: "2345"

View File

@ -3,9 +3,10 @@ import Config
# Configure your database
config :friends, Friends.Repo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "friends_dev",
password: "pleasework",
hostname: "10.0.0.22",
database: "friends_crm",
port: "2345",
stacktrace: true,
show_sensitive_data_on_connection_error: true,
pool_size: 10
@ -19,7 +20,7 @@ config :friends, Friends.Repo,
config :friends, FriendsWeb.Endpoint,
# Binding to loopback ipv4 address prevents access from other machines.
# Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
http: [ip: {127, 0, 0, 1}, port: 4000],
http: [ip: {0, 0, 0, 0}, port: 4000],
check_origin: false,
code_reloader: true,
debug_errors: true,

View File

@ -0,0 +1,119 @@
defmodule Friends.Friend do
use Ecto.Schema
alias Friends.{Relationship, Friend}
import Helpers
import Ecto.Query
@repo Friends.Repo
schema "friends" do
field(:name, :string)
field(:nickname, :string)
field(:born, :date)
field(:phone, :string)
field(:email, :string)
field(:slug, :string)
field(:memories, {:array, :string})
# has_many(:photos, Media.Photo)
many_to_many(
:relationships,
Friends.Friend,
join_through: Relationship,
join_keys: [friend_id: :id, relation_id: :id]
)
many_to_many(
:reverse_relationships,
Friends.Friend,
join_through: Relationship,
join_keys: [relation_id: :id, friend_id: :id]
)
end
def changeset(friend, params \\ %{}) do
friend
|> Ecto.Changeset.cast(params, [:name, :born, :nickname, :email, :phone, :slug])
|> Ecto.Changeset.validate_required([:name, :email, :phone, :born])
|> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/)
|> Ecto.Changeset.validate_format(:email, Regex.compile!("^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"))
|> Ecto.Changeset.validate_format(:phone, Regex.compile!("^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$"))
|> Ecto.Changeset.unique_constraint(:name)
end
def all() do
preloads = [:relationships, :reverse_relationships]
@repo.all(from(f in Friends.Friend, preload: ^preloads))
end
def new(params \\ %{}) do
%Friend{id: "new"}
|> struct(params)
end
def get_by_slug(slug) do
@repo.one(
from(f in Friend,
where: f.slug == ^slug,
preload: [:relationships, :reverse_relationships]
)
)
end
def get_by_id(id) do
@repo.one(
from(f in Friend,
where: f.id == ^id,
preload: [:relationships, :reverse_relationships]
)
)
end
def create_or_update(params) do
case params.id do
"new" ->
%Friend{} |> Friend.changeset(%{params | id: nil})
|> Map.put(:action, :insert)
|> @repo.insert!
_number ->
Friend.get_by_id(params.id |> String.to_integer)
|> Friend.changeset(params)
|> Map.put(:action, :update)
|> @repo.update!
end
end
def get_relationships(friend) do
friend
|> relations
|> Enum.map(&(relation(friend, &1)))
end
def get_events(friend) do
friend
|> get_relationships
|> Enum.map(&(&1.events))
|> List.flatten
end
def age(friend) do
# Age in years
Date.diff(Date.utc_today,friend.born) |> div(365)
end
# TODO: Refactor
def create_birth_event(friend) do
if is_nil @repo.all(
from(e in Friends.Event,
where: e.name == "born"
)) |> List.flatten do
else
"find"
end
end
end

View File

@ -0,0 +1,133 @@
defmodule Friends.Relationship do
use Ecto.Schema
import Ecto.Query
alias Friends.{Relationship,Event,Friend}
@repo Friends.Repo
schema "relationships" do
field(:friend_id, :id)
field(:relation_id, :id)
field(:type, :integer)
has_many(:events, Friends.Event)
end
@attrs [:friend_id, :relation_id]
def types(index) do
types() |> elem(index)
end
def types do
# Tuple: name of the type, associated color, and what that person "is" to the other
{
{:acquaintances, :info, nil},
{:family, :primary, :relative},
{:friends, :secondary, :friend},
{:partners, :info, :partner},
{:dating, :success, :date},
{:engaged, :success, :fiancé},
{:married, :success, :spouse},
{:divorced, :error, :ex},
{:complicated, :warning, :something},
{:cofounders, :accent, :cofounder}
}
end
def get_type(rel) do
rel.type |> types |> elem(0)
end
def get_color(rel) do
rel.type |> types |> elem(1)
end
def get_relation(rel) do
rel.type |> types |> elem(2)
end
def defining_event(rel) do
@repo.one(
from(e in Event,
where: e.relationship_id == ^rel.id and e.defining
)
)
end
def changeset(struct, params \\ %{}) do
struct
|> Ecto.Changeset.cast(params, @attrs)
|> Ecto.Changeset.unique_constraint(
[:friend_id, :relation_id],
name: :relationships_friend_id_relation_id_index
)
|> Ecto.Changeset.unique_constraint(
[:relation_id, :friend_id],
name: :relationships_relation_id_friend_id_index
)
end
def new(friend1, friend2, type \\ 0) do
id1 = friend1.id
id2 = friend2.id
{:ok, relationship} = @repo.insert(
%Relationship{
friend_id: id1,
relation_id: id2,
type: type
}
)
relationship
end
def get(friend1, friend2) do
id1 = friend1.id
id2 = friend2.id
rel = @repo.one(
from(r in Relationship,
where: r.friend_id == ^id1 and r.relation_id == ^id2,
preload: [:events]
)
)
if rel == nil do
@repo.one(
from(r in Relationship,
where: r.friend_id == ^id2 and r.relation_id == ^id1,
preload: [:events]
)
)
else
rel
end
end
def get_or_new(a,b) do
case get(a, b) do
nil -> new(a,b)
relationship -> relationship
end
end
def get_by_slugs([slug1, slug2]) do
friend1 = slug1 |> Friend.get_by_slug
friend2 = slug2 |> Friend.get_by_slug
get(friend1, friend2)
end
def members(rel) do
[
rel.friend_id,
rel.relation_id
]
|> Enum.map(&Friend.get_by_id/1)
end
def age(relationship) do
relationship.events
|> Enum.map(fn(event) ->
Date.diff(Date.utc_today, event.date)
end) |> Enum.sort |> List.last |> div(365)
end
end

View File

@ -0,0 +1,78 @@
defmodule Helpers do
import Helpers.Names
def do!(thing) do
case thing do
{:ok, answer} -> answer
{_, _error} -> nil
end
end
def pluralize(qty, word) do
plural = if qty == 1 do
"#{word}"
else
"#{word}s"
end
"#{qty} #{plural}"
end
def parse_date(str), do: str |> to_string |> Timex.parse("%Y-%m-%d", :strftime)
def format_phone(str) do
str
|> String.replace(" ", "") |> String.replace("-", "") |> String.replace(".", "")
end
def to_slug(name) when is_binary(name) do
name
|> String.replace(" ", "-")
|> String.downcase()
end
def to_slug(obj), do: to_slug(obj.name)
def from_slug(name) do
name
|> String.replace("-", " ")
|> String.split(" ")
|> Enum.map(&:string.titlecase/1)
|> Enum.join(" ")
end
def birthday(friend) do
this_year = (Date.utc_today
|> Calendar.strftime("%Y")
|> String.to_integer)
next_birthday = friend.born
|> Calendar.strftime("%m-%d")
next_birthdate = "#{this_year}-#{next_birthday}"
|> Date.from_iso8601!()
if next_birthdate |> Date.diff(Date.utc_today) < 0 do
"#{this_year + 1}-#{next_birthday}" |> Date.from_iso8601!()
else
next_birthdate
end
end
def time_until_birthday(friend) do
birthday(friend) |> Date.diff(Date.utc_today)
end
def relations(friend) do
[friend.relationships, friend.reverse_relationships]
|> List.flatten()
end
def relation(friend, friend2) do
Friends.Relationship.get(friend, friend2)
end
def events(relationship) do
relationship.events
end
end

View File

@ -0,0 +1,34 @@
defmodule Helpers.Names do
def full_name(friend) do
friend.name
|> String.reverse()
|> String.split(" ", parts: 2)
|> Enum.map(&String.reverse/1)
|> Enum.reverse()
# This is all so that "First Middle Last" becomes ["First Middle", "Last"]
# Is there a more elegant solution? DEFINITELY.
end
def first_name(friend) do
friend
|> first_and_middle_name()
|> String.split()
|> List.first()
end
def first_and_middle_name(friend) do
friend
|> full_name
|> List.first()
end
def middle_name(friend) do
[_first | middles] = friend |> first_and_middle_name() |> String.split()
middles |> Enum.join(" ")
end
def last_name(friend) do
friend |> full_name |> List.last()
end
end

View File

@ -7,7 +7,7 @@ defmodule Friends.MixProject do
version: "0.1.0",
elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:gettext] ++ Mix.compilers(),
compilers: Mix.compilers(),
start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps()
@ -20,7 +20,7 @@ defmodule Friends.MixProject do
def application do
[
mod: {Friends.Application, []},
extra_applications: [:logger, :runtime_tools]
extra_applications: [:logger, :runtime_tools, :timex, :yamerl]
]
end
@ -48,7 +48,12 @@ defmodule Friends.MixProject do
{:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.18"},
{:jason, "~> 1.2"},
{:plug_cowboy, "~> 2.5"}
{:plug_cowboy, "~> 2.5"},
{:timex, "~> 2.1.6"},
{:tailwind, "~> 0.1.6", runtime: Mix.env() == :dev},
{:earmark, "~> 1.4"},
{:html_sanitize_ex, "~> 1.3"},
{:yamerl, github: "yakaz/yamerl"}
]
end

View File

@ -1,20 +1,31 @@
%{
"castore": {:hex, :castore, "0.1.18", "deb5b9ab02400561b6f5708f3e7660fc35ca2d51bfc6a940d2f513f89c2975fc", [:mix], [], "hexpm", "61bbaf6452b782ef80b33cdb45701afbcf0a918a45ebe7e73f1130d661e66a06"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"earmark": {:hex, :earmark, "1.4.31", "1125967a078b5ab8a0a5d3639d6de9c2b7538f84782e4528374c7b6ab9e61f1e", [:mix], [{:earmark_parser, "~> 1.4.29", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "d334b814deb75bc4999b1d447fc200690de1d2983076314eacafd61e5f38dba1"},
"earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
"ecto": {:hex, :ecto, "3.9.1", "67173b1687afeb68ce805ee7420b4261649d5e2deed8fe5550df23bab0bc4396", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c80bb3d736648df790f7f92f81b36c922d9dd3203ca65be4ff01d067f54eb304"},
"ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"},
"esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"},
"gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.2", "c479398b6de798c03eb5d04a0a9a9159d73508f83f6590a00b8eacba3619cf4c", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "aef6c28585d06a9109ad591507e508854c5559561f950bbaea773900dd369b0e"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mochiweb": {:hex, :mochiweb, "2.22.0", "f104d6747c01a330c38613561977e565b788b9170055c5241ac9dd6e4617cba5", [:rebar3], [], "hexpm", "cbbd1fd315d283c576d1c8a13e0738f6dafb63dc840611249608697502a07655"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.6.14", "57678366dc1d5bad49832a0fc7f12c2830c10d3eacfad681bfe9602cd4445f04", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d48c0da00b3d4cd1aad6055387917491af9f6e1f1e96cedf6c6b7998df9dba26"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
@ -28,8 +39,14 @@
"plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"swoosh": {:hex, :swoosh, "1.8.1", "b1694d57c01852f50f7d4e6a74f5d119b2d8722d6a82f7288703c3e448ddbbf8", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e64a93d71d1e1e32db681cf7870697495c9cb2df4a5484f4d91ded326ccd3cbb"},
"tailwind": {:hex, :tailwind, "0.1.9", "25ba09d42f7bfabe170eb67683a76d6ec2061952dc9bd263a52a99ba3d24bd4d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "9213f87709c458aaec313bb5f2df2b4d2cedc2b630e4ae821bf3c54c47a56d0b"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
"timex": {:hex, :timex, "2.1.6", "2c59cd03074bccea47acd668c4dd6aad269879bcc9d6d4dd98fe0ffbaf48fcaa", [:mix], [{:combine, "~> 0.7", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "c0e3b74beb0734f0602eed0de5bbcce984fc435f258c974bde4169a407330d12"},
"tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
"yamerl": {:git, "https://github.com/yakaz/yamerl.git", "bf9d8b743bfc9775f2ddad9fb8d18ba5dc29d3e1", []},
}

View File

@ -0,0 +1,12 @@
defmodule Friends.Repo.Migrations.CreateFriends do
use Ecto.Migration
def change do
create table(:friends) do
add :name, :string
add :nickname, :string
add :born, :date
add :memories, {:array, :string}
end
end
end

View File

@ -0,0 +1,25 @@
defmodule Friends.Repo.Migrations.CreateRelationships do
use Ecto.Migration
def change do
create table(:relationships) do
add :friend_id, references(:friends)
add :relation_id, references(:friends)
end
create index(:relationships, [:friend_id])
create index(:relationships, [:relation_id])
create unique_index(
:relationships,
[:friend_id, :relation_id],
name: :relationships_friend_id_relation_id_index
)
create unique_index(
:relationships,
[:relation_id, :friend_id],
name: :relationships_relation_id_friend_id_index
)
end
end

View File

@ -0,0 +1,10 @@
defmodule Friends.Repo.Migrations.CreatePlaces do
use Ecto.Migration
def change do
create table(:places) do
add :name, :string
add :type, :string
end
end
end

View File

@ -0,0 +1,12 @@
defmodule Friends.Repo.Migrations.CreateEncounters do
use Ecto.Migration
def change do
create table(:encounters) do
add :relationship_id, references(:relationships)
add :date, :date
add :story, :string
add :place_id, references(:places)
end
end
end

View File

@ -0,0 +1,10 @@
defmodule Friends.Repo.Migrations.AddEmailsAndPhonesToFriends do
use Ecto.Migration
def change do
alter table("friends") do
add :email, :string
add :phone, :string
end
end
end

View File

@ -0,0 +1,7 @@
defmodule Friends.Repo.Migrations.MakeNamesUnique do
use Ecto.Migration
def change do
create unique_index(:friends, [:name])
end
end

View File

@ -0,0 +1,9 @@
defmodule Friends.Repo.Migrations.FriendsHaveSlugs do
use Ecto.Migration
def change do
alter table("friends") do
add :slug, :string
end
end
end

View File

@ -0,0 +1,7 @@
defmodule Friends.Repo.Migrations.RenameEncountersToEvents do
use Ecto.Migration
def change do
rename table(:encounters), to: table(:events)
end
end

View File

@ -0,0 +1,13 @@
defmodule Friends.Repo.Migrations.AddRelationshipTypeAndDefiningEvent do
use Ecto.Migration
def change do
alter table("events") do
add :name, :string
add :defining, :boolean
end
alter table("relationships") do
add :type, :integer
end
end
end

View File

@ -0,0 +1,9 @@
defmodule Friends.Repo.Migrations.AddLatlonToPlaces do
use Ecto.Migration
def change do
alter table("places") do
add :latlon, {:array, :float}
end
end
end

View File

@ -0,0 +1,9 @@
defmodule Friends.Repo.Migrations.AddZoomToPlaces do
use Ecto.Migration
def change do
alter table("places") do
add :zoom, :integer
end
end
end

View File

@ -0,0 +1,9 @@
defmodule Friends.Repo.Migrations.AddSelfToEvents do
use Ecto.Migration
def change do
alter table("events") do
add :solo, :boolean
end
end
end