From 4a37cc402a231648f25e0db493056786bc5f36f7 Mon Sep 17 00:00:00 2001 From: Nikos Papadakis Date: Mon, 20 Nov 2023 18:50:24 +0200 Subject: [PATCH] draft: apps --- app/lib/prymn/apps.ex | 103 ++++++++++++++++++ app/lib/prymn/apps/app.ex | 17 +++ app/lib/prymn_web/components/create_app.ex | 78 +++++++++++++ app/lib/prymn_web/live/app_index_live.ex | 63 +++++++++++ app/lib/prymn_web/router.ex | 3 + app/test/prymn/apps_test.exs | 59 ++++++++++ app/test/support/fixtures/apps_fixtures.ex | 20 ++++ .../migrations/20231119141943_create_apps.exs | 11 ++ 8 files changed, 354 insertions(+) create mode 100644 app/lib/prymn/apps.ex create mode 100644 app/lib/prymn/apps/app.ex create mode 100644 app/lib/prymn_web/components/create_app.ex create mode 100644 app/lib/prymn_web/live/app_index_live.ex create mode 100644 app/test/prymn/apps_test.exs create mode 100644 app/test/support/fixtures/apps_fixtures.ex create mode 100644 priv/repo/migrations/20231119141943_create_apps.exs diff --git a/app/lib/prymn/apps.ex b/app/lib/prymn/apps.ex new file mode 100644 index 0000000..db51b1c --- /dev/null +++ b/app/lib/prymn/apps.ex @@ -0,0 +1,103 @@ +defmodule Prymn.Apps do + @moduledoc """ + The Apps context. + """ + + import Ecto.Query, warn: false + alias Prymn.Repo + alias Prymn.Apps.App + + @doc """ + Returns the list of apps. + + ## Examples + + iex> list_apps() + [%App{}, ...] + + """ + def list_apps do + Repo.all(App) + end + + @doc """ + Gets a single app. + + Raises `Ecto.NoResultsError` if the App does not exist. + + ## Examples + + iex> get_app!(123) + %App{} + + iex> get_app!(456) + ** (Ecto.NoResultsError) + + """ + def get_app!(id), do: Repo.get!(App, id) + + @doc """ + Creates a app. + + ## Examples + + iex> create_app(%{field: value}) + {:ok, %App{}} + + iex> create_app(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_app(attrs \\ %{}) do + %App{} + |> App.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a app. + + ## Examples + + iex> update_app(app, %{field: new_value}) + {:ok, %App{}} + + iex> update_app(app, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_app(%App{} = app, attrs) do + app + |> App.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a app. + + ## Examples + + iex> delete_app(app) + {:ok, %App{}} + + iex> delete_app(app) + {:error, %Ecto.Changeset{}} + + """ + def delete_app(%App{} = app) do + Repo.delete(app) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking app changes. + + ## Examples + + iex> change_app(app) + %Ecto.Changeset{data: %App{}} + + """ + def change_app(%App{} = app, attrs \\ %{}) do + App.changeset(app, attrs) + end +end diff --git a/app/lib/prymn/apps/app.ex b/app/lib/prymn/apps/app.ex new file mode 100644 index 0000000..77a9943 --- /dev/null +++ b/app/lib/prymn/apps/app.ex @@ -0,0 +1,17 @@ +defmodule Prymn.Apps.App do + use Ecto.Schema + import Ecto.Changeset + + schema "apps" do + field :name, :string + + timestamps() + end + + @doc false + def changeset(app, attrs) do + app + |> cast(attrs, [:name]) + |> validate_required([:name]) + end +end diff --git a/app/lib/prymn_web/components/create_app.ex b/app/lib/prymn_web/components/create_app.ex new file mode 100644 index 0000000..cfeb448 --- /dev/null +++ b/app/lib/prymn_web/components/create_app.ex @@ -0,0 +1,78 @@ +defmodule PrymnWeb.CreateApp do + use PrymnWeb, :live_component + + @impl true + def render(assigns) do + ~H""" +
+
+ App Type + + + + + + + + +
+ <.wordpress_app_form :if={assigns.app_type == "wordpress"} servers={assigns[:servers]} /> +
+ """ + end + + defp wordpress_app_form(assigns) do + ~H""" + <.simple_form id="test" for={nil}> + <.input + :if={assigns.servers != nil} + id="server" + type="select" + name="server" + prompt="Select a server to host this app..." + options={[]} + value={nil} + label="Hosting Server" + /> + <.input id="name" type="text" name="app_name" value={nil} label="WordPress Site Name" required /> + <.input id="domain" type="text" name="domain" value={nil} label="Domain Name" required /> + <.input type="checkbox" name="create_database?" label="Create a new database?" /> + <.input type="text" name="db_name" value={nil} label="Database name" /> + + <.input type="select" name="db_name" value={nil} options={["db1", "db2"]} label="Database" /> + + <.input type="text" name="admin_username" value={nil} label="Admin Username" /> + <.input type="email" name="admin_email" value={nil} label="Admin Email" /> + <.input type="password" name="admin_password" value={nil} label="Admin Password" /> + + <.button type="submit">Create + + """ + end +end diff --git a/app/lib/prymn_web/live/app_index_live.ex b/app/lib/prymn_web/live/app_index_live.ex new file mode 100644 index 0000000..76ad2d7 --- /dev/null +++ b/app/lib/prymn_web/live/app_index_live.ex @@ -0,0 +1,63 @@ +defmodule PrymnWeb.AppIndexLive do + use PrymnWeb, :live_view + + @impl true + def mount(_, _, socket) do + apps = Prymn.Apps.list_apps() + servers = Prymn.Servers.list_servers() + + {:ok, + socket + |> assign(:servers, servers) + |> assign(:apps, apps)} + end + + @impl true + def render(assigns) do + ~H""" + <%= cond do %> + <% assigns.live_action == :new -> %> + <.back navigate={~p"/apps"}>Go back + <.live_component + id={:new} + module={PrymnWeb.CreateApp} + app_type={assigns[:app_type]} + servers={@servers} + /> + <% assigns.apps == [] -> %> + <.onboarding /> + <% true -> %> +
+ <.header> + Live Apps + <:subtitle> + All of your apps accross all projects. + + +
+ <% end %> + """ + end + + @impl true + def handle_params(%{"app_type" => app_type}, _, socket) do + {:noreply, assign(socket, app_type: app_type)} + end + + def handle_params(_, _, socket) do + {:noreply, + socket + |> assign(:page_title, (socket.assigns.live_action == :new && "New App") || "Apps")} + end + + defp onboarding(assigns) do + ~H""" +
+

You have no Apps.

+ <.button type="link" patch={~p"/apps/new"}> + <.icon class="mr-2 h-6 w-6" name="hero-plus" /> Create your first App! + +
+ """ + end +end diff --git a/app/lib/prymn_web/router.ex b/app/lib/prymn_web/router.ex index 21ccd5c..ef30200 100644 --- a/app/lib/prymn_web/router.ex +++ b/app/lib/prymn_web/router.ex @@ -37,6 +37,9 @@ defmodule PrymnWeb.Router do live "/servers", ServerLive.Index, :index live "/servers/new", ServerLive.Index, :new live "/servers/:id", ServerLive.Show + + live "/apps", AppIndexLive + live "/apps/new", AppIndexLive, :new end end diff --git a/app/test/prymn/apps_test.exs b/app/test/prymn/apps_test.exs new file mode 100644 index 0000000..1d31f6e --- /dev/null +++ b/app/test/prymn/apps_test.exs @@ -0,0 +1,59 @@ +defmodule Prymn.AppsTest do + use Prymn.DataCase + + alias Prymn.Apps + + describe "apps" do + alias Prymn.Apps.App + + import Prymn.AppsFixtures + + @invalid_attrs %{name: nil} + + test "list_apps/0 returns all apps" do + app = app_fixture() + assert Apps.list_apps() == [app] + end + + test "get_app!/1 returns the app with given id" do + app = app_fixture() + assert Apps.get_app!(app.id) == app + end + + test "create_app/1 with valid data creates a app" do + valid_attrs = %{name: "some name"} + + assert {:ok, %App{} = app} = Apps.create_app(valid_attrs) + assert app.name == "some name" + end + + test "create_app/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Apps.create_app(@invalid_attrs) + end + + test "update_app/2 with valid data updates the app" do + app = app_fixture() + update_attrs = %{name: "some updated name"} + + assert {:ok, %App{} = app} = Apps.update_app(app, update_attrs) + assert app.name == "some updated name" + end + + test "update_app/2 with invalid data returns error changeset" do + app = app_fixture() + assert {:error, %Ecto.Changeset{}} = Apps.update_app(app, @invalid_attrs) + assert app == Apps.get_app!(app.id) + end + + test "delete_app/1 deletes the app" do + app = app_fixture() + assert {:ok, %App{}} = Apps.delete_app(app) + assert_raise Ecto.NoResultsError, fn -> Apps.get_app!(app.id) end + end + + test "change_app/1 returns a app changeset" do + app = app_fixture() + assert %Ecto.Changeset{} = Apps.change_app(app) + end + end +end diff --git a/app/test/support/fixtures/apps_fixtures.ex b/app/test/support/fixtures/apps_fixtures.ex new file mode 100644 index 0000000..83e103f --- /dev/null +++ b/app/test/support/fixtures/apps_fixtures.ex @@ -0,0 +1,20 @@ +defmodule Prymn.AppsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `Prymn.Apps` context. + """ + + @doc """ + Generate a app. + """ + def app_fixture(attrs \\ %{}) do + {:ok, app} = + attrs + |> Enum.into(%{ + name: "some name" + }) + |> Prymn.Apps.create_app() + + app + end +end diff --git a/priv/repo/migrations/20231119141943_create_apps.exs b/priv/repo/migrations/20231119141943_create_apps.exs new file mode 100644 index 0000000..7b1e392 --- /dev/null +++ b/priv/repo/migrations/20231119141943_create_apps.exs @@ -0,0 +1,11 @@ +defmodule Prymn.Repo.Migrations.CreateApps do + use Ecto.Migration + + def change do + create table(:apps) do + add :name, :string + + timestamps() + end + end +end