Backpex - a highly customizable admin panel for Phoenix LiveView applications

Thanks! :slightly_smiling_face: Unfortunately, Backpex does not support Ash at the moment.

Thanks for bringing live_admin to the table. We started Backpex before we released the project to GitHub, so we started with Backpex around the same time as live_admin.

We had really specific customization needs for our customers, so one reason we built Backpex was to have something under our control.

If you want to quickly add a UI for your database, live_admin seems like a good choice. However, the focus for Backpex is to be able to serve not only as an admin UI, but also as a standalone admin application that can be used by non-developers.

I havenā€™t looked at the latest version of live_admin in detail yet, so I canā€™t compare it to Backpex on a feature level, but we have detailed guides that show our features. So if you are familiar with live_admin you should be able to figure out the differences quickly.

3 Likes

A back office will be always required. We currently build many API products as most enterprises are moving toward API first design but you still need those screens that will be required by support teams or others to view configuration , in/out requests , related resources , ā€¦etc and even screens to do manual updates not figured out during initial API design but needs to be done now manually in a controlled way.

1 Like

Backpex v0.5 release

Today we released Backpex version 0.5. This version mainly fixes the problems you had during installation, improves the documentation and already includes some feature requests :rocket:

One of the biggest improvements is that with 0.5 we support other ID types besides binary_id for resources. This makes it easier to implement Backpex LiveResources for existing data.

We have also changed the way Backpex integrates heroicons. We now require our users to track the heroicons GitHub repository and generate the styles we need. Itā€™s a bit more configuration, but itā€™s the way it works in new Phoenix applications. This eliminates dependency conflicts regarding heroicons in new Phoenix applications and makes Backpex installation more seamless for many projects.

We also improved the documentation and shipped some new features to make LiveResources even more configurable.

Release Notes: Release 0.5.0 Ā· naymspace/backpex Ā· GitHub

Upgrade Guide: Upgrading to v0.5 ā€” Backpex v0.5.1

6 Likes

Iā€™ve got this reply from Zach on Slack.

ā€œThey are pretty tightly coupled directly to ecto TBH
You could use ecto changeset functions and use backpex
but youā€™d be allowing direct edit access, it wouldnā€™t be benefitting from anything that Ash providesā€

So i guess yes but bypassing Ash, really. I would love an integration aswell, but i might just go with the hacked version for now. :slight_smile:

1 Like

This is awesome but unfortunately I canā€™t use it right now. SQLite support would be amazing. Any plans for it? :slight_smile:

2 Likes

There is a discussion on GitHub about SQLite support. It is not on our roadmap as we use PostgreSQL in all our projects, but we would like to see a pull request to add support for it. Maybe someone who uses SQLite in their project is up for it?

Is there a support channel for backpex related queries?

You can use our GitHub discussions board.

1 Like

Backpex v0.6 release

Weā€™ve just released Backpex v0.6. One of the big improvements in this release is full daisyUI theme support (thanks to @thomasfortes) :art: A theme switch for our demo application is already in the works. We also worked on some user feedback, like displaying additional labels inside the filter inputs.

Additionally, this release contains many bug fixes and some documentation improvements.

Release Notes: Release 0.6.0 Ā· naymspace/backpex Ā· GitHub

Upgrade Guide: Upgrading to v0.6 ā€” Backpex v0.6.0

5 Likes

Backpex v0.7 release

After two months with version 0.6, we are happy to announce the release of Backpex v0.7!

Key highlights:

  1. Refactored Backpex.Fields.HasMany: This update improves the functionality of the HasMany field type.
  2. Improved documentation: The installation guide and overall documentation have been refined for better clarity.
  3. Bug fixes: Several bugs have been fixed, including issues with HasMany and MultiSelect search fields, and index form updates.

Breaking changes:

  • Removed automatic formatting of integer values
  • Updated item actionsā€™ label and icon functions to receive the item
  • Refactored Backpex.Fields.HasMany
  • Updated calls to Backpex.Field.handle_index_editable/2

Please refer to the Upgrade Guide for detailed instructions on upgrading to v0.7.

Other improvements:

  • Consistent code formatting style
  • Dependency updates, including major version bumps for several packages
  • Demo application enhancements, including a daisyUI theme selector

Weā€™ve also made numerous minor bug fixes and improvements. See the full changelog for a complete list of changes.

Weā€™d like to thank all contributors, especially our new ones! As always, we appreciate your feedback.

Release Notes: Release 0.7.0 Ā· naymspace/backpex Ā· GitHub

Upgrade Guide: Upgrading to v0.7 ā€” Backpex v0.7.0

4 Likes

Thanks, that looks great!

Thanks, Iā€™m really excited to try this in my app!

Just wondering - the current installation guide says to follow DaisyUIā€™s instruction installations. But when I follow those instructions as written, it applies DaisyUI styles to my entire app, not just the admin pages, which isnā€™t what I want as my main app already has its own styles.

Is there a simple way to install DaisyUI so that it only applies to my Backpex routes and not my whole?

I came up with my own solution involving making a separate tailwind config file called tailwind.backpex.config.js, compiling another CSS file thatā€™s separate from the normal app.css then rendering my backpex routes with a layout that only loads the new CSS file. It works, but Iā€™m just wondering if thereā€™s some obvious simpler way that I donā€™t know about.

If not Iā€™m happy to write up my own solution in more detail, maybe as a PR for the Backpex docs?

I also run two different tailwind configs for admin and the main site, I also run two different javascript files because some custom fields have deps that I donā€™t need in the main site and I probably do the same thing that you do, split the files and edit the config and aliases.

I donā€™t think thereā€™s anything wrong with it, the tailwind docs even provide a way to use different configs when using the tailwind cli without passing it in the command line.

1 Like

Okay so if anyone else is wondering how to do this, hereā€™s what I did:

  1. copy assets/tailwind.config.js to a new file assets/tailwind.backpex.config.js.
  2. Update the new config file like so:
  content: [
    "./js/**/*.js",
    "../lib/*_web.ex",
    "../lib/*_web/**/*.*ex",
+   "../deps/backpex/**/*.*ex"
  ],
  ā€¦
  plugins: [
+   require('daisyui')
    ā€¦
  1. Add a new tailwind command in config/config.exs that compiles backpex-related CSS (including DaisyUI) to backpex.css:
 config :tailwind,
   version: "3.4.0",
   default: [
     args: ~w(
       --config=tailwind.config.js
       --input=css/app.css
       --output=../priv/static/assets/app.css
     ),
     cd: Path.expand("../assets", __DIR__)
-  ]
+  ],
+  backpex: [
+    args: ~w(
+      --config=tailwind.backpex.config.js
+      --input=css/app.css
+      --output=../priv/static/assets/backpex.css
+    ),
+    cd: Path.expand("../assets", __DIR__)
+  ]
  1. Add a new watcher in config/dev.exs:
 config :my_app, MyAppWeb.Endpoint,
   ā€¦
   watchers: [
     esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]},
     tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]},
+    tailwind_backpex: {Tailwind, :install_and_run, [:backpex, ~w(--watch)]}
   ]

Then add this line in your backpex layout so it loads the new CSS:

<link phx-track-static rel="stylesheet" href={~p"/assets/backpex.css"} />

Hope that helps someone.

3 Likes

Backpex v0.8 release - Starting to support Ash

Weā€™ve just released a new version of Backpex. We have asked for feedback many times in the past, and by far the most requested feature was Ash support.

So this release lays the foundation to support multiple data sources (e.g. Ash).

With v0.8 we introduce an adapter module that sits between the UI and the data. We tried to move all current Ecto specific code into an Ecto adapter and have already started to implement an Ash adapter.

We added a new helpdesk section to our demo application that lists tickets that are retrieved from an Ash resource.

There is still some Ecto code that we need to move to the Ecto adapter. Moreover, the adapter pattern / API in Backpex is in an early alpha stage and is likely to change in the future.

In addition, the ash adapter is limited to listing and displaying items. You canā€™t edit items, use filters or delete items. Therefore, we recommend that you do not use the Ash adapter in production code. If you use the Ecto adapter, everything works as before.

However, we wanted to release these WIP changes as early as possible to get feedback from you and let you experiment with them. What are you looking for besides editing, filtering and deleting?

This release has some breaking changes, so make sure to read the upgrade guide. The guide also contains more information about the adapter pattern.

Release Notes: Release 0.8.0 Ā· naymspace/backpex Ā· GitHub

Upgrade Guide: Upgrading to v0.8 ā€” Backpex v0.8.0

9 Likes

Does the getting started guide need to be updated with these changes? It looks like the live resource example given in the getting started guide doesnā€™t have the changes applied from the upgrade guide.

EDIT: should also probably change the version listed in the getting started guide to 0.8.0 :slight_smile:

1 Like

Yes, it needs to be updated! Fixed in v0.8.1 :raised_hands:

2 Likes

I really like this Backpex library, and Iā€™d like to ask whether the following feature would be in scope.

Currently, one has to supply a lot of information for each individual resource (repo, layout, topic, event names, list of fields, etc.), which makes sense because it allows Backpex to be decoupled from the userā€™s app.

I wonder if one could introspect the ecto schema and automatically generate the list of fields and the corresponding input types (which could be overridden by the user if needed). Also, we could have a MyApp.LiveResource module which could be used in the individual live resources.

Iā€™m thinking of something like this:

defmodule MyAppWeb.LiveResource do
  use Backpex.AutoLiveResource,
    repo: MyApp.Repo,
    layout: {MyAppWeb.Layouts, :admin}
    # possibly other options, like preferred
    # input types for each ecto field type 
end

Now we can use the above for super concise resource definitions:

defmodule MyAppWeb.MyResourceLive do
  use MyAppWeb.LiveResource,
    resource: MyApp.MyContext.MyResource
    # everything else is taken from the
    # common parameters given to the
    # use macro above or inferred from the
    # resource module name.
    # fields are inferred from the ecto schema
    override1: ...,
    override2: ...
    ...
end

I could implement this if you think itā€™s in scope, otherwise Iā€™ll just implement this in a different package.

I have implemented a new macro, which does pretty much what Iā€™ve explained in the last message:

defmodule MyAppWeb.LiveResource do
  # Could be added into the Backpex namespace too
  use BackpeXtra.LiveResource,
    repo: MyApp.Repo,
    layout: {MyAppWeb.Layouts, :admin},
    pubsub: MyApp.PubSub
end

This module will encapsulate all the infomration thatā€™s specific to your app

The next module will use the above module to very succintly declare a new live resource. Lots of things are inferred automatically from the resource module name.

defmodule MyAppWeb.MyContext.MyResourceLive do
  use MyAppWeb.LiveResource,
    resource: MyApp.MyContext.MyResource
end

The use ... macro above generates the following code:

def __update_changeset(item, attrs, _opts) do
  # The macro has been smart enough to notice that
  # there isn't an `update_changeset/3` function in
  # the ecto schema and that there is a `changeset/2`
  # function. For that reason, we will use that function.
  StartTrials.Roster.Trial.changeset(item, attrs)
end

def __create_changeset(item, attrs, _opts) do
  StartTrials.Roster.Trial.changeset(item, attrs)
end

use Backpex.LiveResource,
  adapter_config: [
    schema: StartTrials.Roster.Trial,
    repo: StartTrials.Repo,
    # Use the wrapper defined in this module, so that we
    # can adapt the `update_changeset/3` function to the
    # functions taht are implemented in the ecto schema.
    update_changeset: &__MODULE__.__update_changeset/3,
    create_changeset: &__MODULE__.__create_changeset/3,
    item_query: &__MODULE__.item_query/3
  ],
  layout: {StartTrialsWeb.Layouts, :admin},
  # topic and event_prefix names inferred from the resource module
  pubsub: [name: StartTrials.PubSub, topic: "trial", event_prefix: "trial_"]

@impl Backpex.LiveResource
def singular_name do
  # Inferred from the resource module
  "Trial"
end

@impl Backpex.LiveResource
def plural_name do
  # Inferred from the resource module
  "Trials"
end

@impl Backpex.LiveResource
def fields do
  # List of fields built automatically from introspecting the
  # fields in the ecto schema. 
  [
    description: %{label: "Description", module: Backpex.Fields.Text},
    name: %{label: "Name", module: Backpex.Fields.Text},
    inserted_at: %{
      except: [:new],
      format: "%Y-%m-%d %H:%M:%S",
      label: "Inserted at",
      module: Backpex.Fields.DateTime,
      readonly: true
    },
    updated_at: %{
      except: [:new],
      format: "%Y-%m-%d %H:%M:%S",
      label: "Updated at",
      module: Backpex.Fields.DateTime,
      readonly: true
    }
  ]
end

# Make this functioin overridable so that the user can implement
# their own fields if our automatic implementation is not a good fit
defoverridable fields: 0

Now, I plan on implementing overrides for everything so that the initial functionality can be replicated with a minimum amount of code. My goal is to be as succint as this: Introduction ā€” Flask AppBuilder (Python code)

PS: the generated code at the bottom was obviously generated from a different macro evocation, I canā€™t edit it now but it does get the idea across

2 Likes