Skip to content

Instantly share code, notes, and snippets.

@nileshtrivedi
Created June 11, 2025 11:57
Show Gist options
  • Save nileshtrivedi/02de3a7f697b14765a3fa04a4f74875d to your computer and use it in GitHub Desktop.
Save nileshtrivedi/02de3a7f697b14765a3fa04a4f74875d to your computer and use it in GitHub Desktop.

Revisions

  1. nileshtrivedi created this gist Jun 11, 2025.
    223 changes: 223 additions & 0 deletions app.exs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,223 @@
    Mix.install([
    {:ash, "~> 3.0"},
    {:phoenix_playground, "== 0.1.6"},
    ], consolidate_protocols: false)

    defmodule Helpdesk do
    end

    defmodule Helpdesk.Support do
    use Ash.Domain, validate_config_inclusion?: false

    resources do
    resource Helpdesk.Support.Todo
    end
    end

    defmodule Helpdesk.Support.Todo do
    # This turns this module into a resource
    use Ash.Resource, domain: Helpdesk.Support, data_layer: Ash.DataLayer.Ets

    actions do
    defaults [:read, :destroy]

    create :add do
    accept [:task, :user_id]
    change set_attribute(:status, "pending")
    end

    update :toggle do
    accept []

    change atomic_update(:status, expr(if status == :pending, do: :done, else: :pending))
    end
    end

    attributes do
    uuid_primary_key :id

    attribute :user_id, :string do
    allow_nil? false
    public? true
    end

    attribute :task, :string do
    allow_nil? false
    public? true
    end

    attribute :status, :string do
    default "pending"
    allow_nil? false
    public? true
    end

    validations do
    validate attribute_in(:status, ["pending", "done"])
    end
    end
    end

    defmodule TodoLive do
    use Phoenix.LiveView
    require Ash.Query

    def get_user_todos(user_id) do
    Helpdesk.Support.Todo
    |> Ash.Query.filter(user_id == ^user_id)
    |> Ash.read!()
    end

    def mount(_params, _session, socket) do
    user_id = "foobar"
    todos = get_user_todos(user_id)
    {:ok, assign(socket, todos: todos, user_id: user_id, new_todo_name: "drink water")}
    end

    def handle_event("create_todo", %{"name" => name}, socket) do

    if name == "" do
    {:noreply, socket}
    else
    # Create new todo with current user's ID
    Helpdesk.Support.Todo
    |> Ash.Changeset.for_create(:add, %{task: String.trim(name), user_id: "foobar"})
    |> Ash.create!()

    # Update socket with new list of todos
    socket =
    socket
    |> assign(:todos, get_user_todos(socket.assigns.user_id))
    |> assign(:new_todo_name, "")

    {:noreply, socket}
    end
    end

    def handle_event("toggle_todo", %{"id" => id}, socket) do
    # Find the todo and ensure it belongs to current user
    todo = Helpdesk.Support.Todo
    |> Ash.Query.filter(user_id == ^socket.assigns.user_id)
    |> Ash.Query.filter(id == ^id)
    |> Ash.read_one!()

    # Update todo
    {:ok, _todo} =
    todo
    |> Ash.Changeset.for_update(:toggle)
    |> Ash.update!()

    # Refresh todos
    {:noreply, assign(socket, :todos, get_user_todos(socket.assigns.user_id))}
    end

    def handle_event("delete_todo", %{"id" => id}, socket) do
    # Delete todo, ensuring it belongs to current user
    todo = Helpdesk.Support.Todo
    |> Ash.Query.filter(user_id == ^socket.assigns.user_id)
    |> Ash.Query.filter(id == ^id)
    |> Ash.read_one!()

    todo
    |> Ash.Changeset.for_destroy(:destroy)
    |> Ash.destroy!()

    # Refresh todos
    {:noreply, assign(socket, :todos, get_user_todos(socket.assigns.user_id))}
    end

    def render(assigns) do
    ~H"""
    <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
    <div class="max-w-md mx-auto mt-10 p-6 bg-gray-200 rounded-lg shadow-md">
    <h1 class="text-2xl font-bold mb-4">Todo List for <%= @user_id %></h1>
    <form phx-submit="create_todo" class="mb-4 flex">
    <input
    type="text"
    name="name"
    value={@new_todo_name}
    placeholder="Enter a new todo"
    class="flex-grow mr-2 px-2 py-1 border rounded"
    />
    <button
    type="submit"
    class="bg-blue-500 text-white px-4 py-1 rounded hover:bg-blue-600"
    >
    Add Todo
    </button>
    </form>
    <ul>
    <%= for todo <- @todos do %>
    <li class="flex items-center justify-between py-2 border-b">
    <label class="flex items-center gap-2">
    <input
    type="checkbox"
    phx-click="toggle_todo"
    phx-value-id={todo.id}
    checked={todo.status == "done"}
    class="mr-2"
    />
    <span class={if todo.status == "done", do: "line-through text-gray-500", else: ""}>
    <%= todo.task %>
    </span>
    <span>by <%= todo.user_id %></span>
    </label>
    <button
    phx-click="delete_todo"
    phx-value-id={todo.id}
    class="text-red-500 hover:text-red-700"
    >
    </button>
    </li>
    <% end %>
    </ul>
    </div>
    """
    end
    end

    defmodule Demo.Router do
    use Phoenix.Router
    import Phoenix.LiveView.Router

    pipeline :browser do
    plug :accepts, ["html"]
    plug :put_secret_key_base
    plug :fetch_session
    plug :put_root_layout, html: {PhoenixPlayground.Layout, :root}
    plug :put_secure_browser_headers
    end

    scope "/" do
    pipe_through :browser

    live "/", TodoLive
    end

    def put_secret_key_base(conn, _) do
    put_in conn.secret_key_base, System.get_env("SECRET_KEY_BASE")
    end
    end

    defmodule Demo.Endpoint do
    use Phoenix.Endpoint, otp_app: :phoenix_playground
    plug Plug.Logger
    socket "/live", Phoenix.LiveView.Socket
    plug Plug.Static, from: {:phoenix, "priv/static"}, at: "/assets/phoenix"
    plug Plug.Static, from: {:phoenix_live_view, "priv/static"}, at: "/assets/phoenix_live_view"
    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
    plug Phoenix.LiveReloader
    plug Phoenix.CodeReloader, reloader: &PhoenixPlayground.CodeReloader.reload/2
    plug Demo.Router
    end

    PhoenixPlayground.start(
    open_browser: false,
    live_reload: true,
    child_specs: [],
    # endpoint: Demo.Endpoint,
    # endpoint_options: [debug_errors: true]
    plug: Demo.Router
    )