diff --git a/assets/css/app.css b/assets/css/app.css new file mode 100644 index 0000000..e69de29 diff --git a/config/config.exs b/config/config.exs index 68366e5..4277de8 100644 --- a/config/config.exs +++ b/config/config.exs @@ -36,7 +36,7 @@ config :esbuild, version: "0.17.11", send_it: [ args: - ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), + ~w(js/app.js css/app.css --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*), cd: Path.expand("../assets", __DIR__), env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ] diff --git a/lib/send_it/marketing.ex b/lib/send_it/marketing.ex new file mode 100644 index 0000000..a7c5d06 --- /dev/null +++ b/lib/send_it/marketing.ex @@ -0,0 +1,199 @@ +defmodule SendIt.Marketing do + @moduledoc """ + The Marketing context. + """ + + import Ecto.Query, warn: false + alias SendIt.Repo + + alias SendIt.Marketing.Contact + alias SendIt.Marketing.Message + + @doc """ + Returns the list of contacts. + + ## Examples + + iex> list_contacts() + [%Contact{}, ...] + + """ + def list_contacts do + Repo.all(Contact) + end + + @doc """ + Gets a single contact. + + Raises `Ecto.NoResultsError` if the Contact does not exist. + + ## Examples + + iex> get_contact!(123) + %Contact{} + + iex> get_contact!(456) + ** (Ecto.NoResultsError) + + """ + def get_contact!(id), do: Repo.get!(Contact, id) + + @doc """ + Creates a contact. + + ## Examples + + iex> create_contact(%{field: value}) + {:ok, %Contact{}} + + iex> create_contact(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_contact(attrs \\ %{}) do + %Contact{} + |> Contact.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a contact. + + ## Examples + + iex> update_contact(contact, %{field: new_value}) + {:ok, %Contact{}} + + iex> update_contact(contact, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_contact(%Contact{} = contact, attrs) do + contact + |> Contact.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a contact. + + ## Examples + + iex> delete_contact(contact) + {:ok, %Contact{}} + + iex> delete_contact(contact) + {:error, %Ecto.Changeset{}} + + """ + def delete_contact(%Contact{} = contact) do + Repo.delete(contact) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking contact changes. + + ## Examples + + iex> change_contact(contact) + %Ecto.Changeset{data: %Contact{}} + + """ + def change_contact(%Contact{} = contact, attrs \\ %{}) do + Contact.changeset(contact, attrs) + end + + @doc """ + Returns the list of messages. + + ## Examples + + iex> list_messages() + [%Message{}, ...] + + """ + def list_messages do + Repo.all(Message) + end + + @doc """ + Gets a single message. + + Raises `Ecto.NoResultsError` if the Message does not exist. + + ## Examples + + iex> get_message!(123) + %Message{} + + iex> get_message!(456) + ** (Ecto.NoResultsError) + + """ + def get_message!(id), do: Repo.get!(Message, id) + + @doc """ + Creates a message. + + ## Examples + + iex> create_message(%{field: value}) + {:ok, %Message{}} + + iex> create_message(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_message(attrs \\ %{}) do + %Message{} + |> Message.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a message. + + ## Examples + + iex> update_message(message, %{field: new_value}) + {:ok, %Message{}} + + iex> update_message(message, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_message(%Message{} = message, attrs) do + message + |> Message.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a message. + + ## Examples + + iex> delete_message(message) + {:ok, %Message{}} + + iex> delete_message(message) + {:error, %Ecto.Changeset{}} + + """ + def delete_message(%Message{} = message) do + Repo.delete(message) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking message changes. + + ## Examples + + iex> change_message(message) + %Ecto.Changeset{data: %Message{}} + + """ + def change_message(%Message{} = message, attrs \\ %{}) do + Message.changeset(message, attrs) + end +end diff --git a/lib/send_it/marketing/contact.ex b/lib/send_it/marketing/contact.ex new file mode 100644 index 0000000..d6ebed3 --- /dev/null +++ b/lib/send_it/marketing/contact.ex @@ -0,0 +1,22 @@ +defmodule SendIt.Marketing.Contact do + use Ecto.Schema + import Ecto.Changeset + + schema "contacts" do + field :name, :string + field :email, :string + field :subscribed, :boolean, default: true + + many_to_many :messages, SendIt.Marketing.Message, join_through: "contacts_messages" + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(contact, attrs) do + contact + |> cast(attrs, [:name, :email, :subscribed]) + |> validate_required([:name, :email, :subscribed]) + |> unique_constraint(:email) + end +end diff --git a/lib/send_it/marketing/message.ex b/lib/send_it/marketing/message.ex new file mode 100644 index 0000000..65771f3 --- /dev/null +++ b/lib/send_it/marketing/message.ex @@ -0,0 +1,20 @@ +defmodule SendIt.Marketing.Message do + use Ecto.Schema + import Ecto.Changeset + + schema "messages" do + field :subject, :string + field :content, :string + + many_to_many :contacts, SendIt.Marketing.Contact, join_through: "contacts_messages" + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(message, attrs) do + message + |> cast(attrs, [:subject, :content]) + |> validate_required([:subject, :content]) + end +end diff --git a/lib/send_it_web/components/core_components.ex b/lib/send_it_web/components/core_components.ex index f28b1eb..2427541 100644 --- a/lib/send_it_web/components/core_components.ex +++ b/lib/send_it_web/components/core_components.ex @@ -156,8 +156,7 @@ defmodule SendItWeb.CoreComponents do phx-connected={hide("#client-error")} hidden > - Attempting to reconnect - <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" /> + Attempting to reconnect <.icon name="hero-arrow-path" class="ml-1 h-3 w-3 animate-spin" /> <.flash diff --git a/lib/send_it_web/components/layouts/app.html.heex b/lib/send_it_web/components/layouts/app.html.heex index e23bfc8..a9efe86 100644 --- a/lib/send_it_web/components/layouts/app.html.heex +++ b/lib/send_it_web/components/layouts/app.html.heex @@ -1,32 +1,4 @@ -
-
-
- - - -

- v<%= Application.spec(:phoenix, :vsn) %> -

-
- -
-
-
-
- <.flash_group flash={@flash} /> - <%= @inner_content %> -
+
+ <.flash_group flash={@flash} /> + <%= @inner_content %>
diff --git a/lib/send_it_web/components/layouts/root.html.heex b/lib/send_it_web/components/layouts/root.html.heex index bc4f7a6..9be4813 100644 --- a/lib/send_it_web/components/layouts/root.html.heex +++ b/lib/send_it_web/components/layouts/root.html.heex @@ -1,5 +1,5 @@ - + @@ -7,47 +7,34 @@ <.live_title suffix=" ยท Phoenix Framework"> <%= assigns[:page_title] || "SendIt" %> - - - -