Kaffy - a quick and flexible admin interface for phoenix applications

Just released v0.7.0. The main feature in this release is scheduled tasks.

Check out the demo and the README section.

def scheduled_tasks(_) do
  [
    %{
      name: "Cache Product Count",
      initial_value: 0,
      every: 300,
      action: fn _v ->
        count = Bakery.Products.count_products()
        {:ok, count}
      end
    },
    %{
      name: "Delete Fake Products",
      every: 60,
      initial_value: nil,
      action: fn _ ->
        Bakery.Products.delete_fake_products()
        {:ok, nil}
      end
    }
  ]
end
9 Likes

Kaffy v0.8.0 has been released with a better UI, custom pages, and more.

New Features

  • ability to add custom links to the side menu.
  • ability to add add custom pages.
  • ability to order records per column in the index page.

Enhancements

  • a placeholder value for :map textarea fields to indicate that JSON content is expected.
  • enhanced “humanization” of field names in index page.
  • improved checkbox form control UI (thanks @areski).
  • new and improved design (thanks @areski).
  • include checkboxes in index page to clearly indicate records are selectable.
  • pagination, filtration, and searching are now bookmarkable with querystring parameters.
  • count query result is now cached if the table has more than 100,000 records (thanks @areski).
  • add option to hide the dashboard menu item.
  • add option to change the root url to be something other than the dashboard.
  • removed render warnings when running under phoenix 1.5.
  • add a much improved date/time picker (thanks @areski).
13 Likes

I’m extremely pleased to announce Kaffy v0.9.0. This has been the biggest release yet with so many contributions from the community.

The main features in this release:

  • Introducing extension modules to add custom css and javascript.
  • Custom form fields for specialized form functionality.
  • List actions now can have an intermediary step for more input from the user.
  • Improved layout for mobile screens.
  • Ability to override Kaffy’s insert, update, and delete functions to customize how Kaffy talks to the repo.
  • Moved scheduled tasks to their own modules and they have their own option in config.
  • Ability to customize the query Kaffy uses for the index and show pages.
  • A more flexible and customizable way to define resources and admin modules.

For a complete list of changes, you can check the repo’s README and CHANGELOG files.
Please note that this is a backwards-incompatible release, but modifications should be quick and minimal.

Many thanks to everyone who contributed in this release.

13 Likes

Oh this is AMAZING – how did I only find out about that now?? :slight_smile:

I remember having this conversation with a ton of developers - most of them maintain this is not needed, they can write it themselves, it’s a blackbox, etc. etc.

But in my experience, in all webapps you need 1) user management and 2) admin interface. And barely ever are your requrements so specific that they justify rewriting all this from scratch over and over again. Therefore I’ve always been a fan of Python/Django having the admin interface included out of the box. In Ruby/Rails there was ActiveAdmin and one more simiar project, but there have always been some problems with them and they had pretty opinionated ugly design :slight_smile: Recently I’ve been a fan of Strapi (built on Koa.js) for quick bootstraping apis (hence the name “stapi”) when prototyping.

What I’m trying to say is thank you, and keep up the great work, it’s worthy!

7 Likes

Thank you! It’s a team effort. Kaffy wouldn’t have reached this stage without the community’s contributions.

I couldn’t agree more :+1:t3:

4 Likes

@aesmail Hey! I am new to Elixir/Phoenix and I am in the process of adding Kaffy to our project. However, I am uncertain of where the admin modules/ widgets logic should live in the application? Could you provide a little more detail?

1 Like

Hey Natasha,

Notice that admin modules are optional. This means that any code essential to running the project or related to the business logic shouldn’t live there and should live somewhere more appropriate (e.g. context modules).

You could place admin modules anywhere in your application and name them anything you want. However, having them under the same namespace as the schemas gives you the convenience of not having to explicitly list them all in the config file. For example, if you have a user schema (MyApp.Accounts.User) and your admin module is (MyApp.Admins.UserAdmin), you need to tell Kaffy where the admin module for the user schema is:

config :kaffy,
  resources: [
    accounts: [
      user: [schema: MyApp.Accounts.User, admin: MyApp.Admins.UserAdmin],
    ]
  ]

Whereas, if you have them both under under the same namespace, you don’t have to do that and Kaffy will detect the admin modules automatically.
schema --> MyApp.Accounts.User
admin ----> MyApp.Accounts.UserAdmin
They are both under the MyApp.Accounts namespace and admin modules must follow the same name as the schema + Admin added to it.

Again, the location of the files doesn’t matter (it’s up to you). The module names are what matter if you want to take advantage of the auto-detection feature.

For functions and business logic, I’d say any function that is beneficial for both your business logic (and maybe Kaffy) should not be in the admin modules. Any function that only works with Kaffy should be in the related admin module. However, you might need to call some business-logic functions inside your admin modules, which is fine IMHO.

Please let me know if you are more confused now :slight_smile:
I feel like I’m not 100% clear and I might have added to your uncertainty.
I’m more than happy to provide more concrete examples if necessary.

2 Likes

Thank you!

1 Like

great admin panel @aesmail, thank you!

1 Like

Hello @aesmail,

I am new to Elixir/Phoenix, and I am trying to build admin panel of some of my modules. But I am seeing something strange on many-to-many association, please have a look at it and let me know what can be possible fixes for this issue.

Hey @kman4you,

Kaffy doesn’t have built-in support for showing m2m associations just yet. It’s a planned feature for a future version. However, you can easily create an admin module and change the value for the “roles” field to be the correct value. Take a look at how you can customize the index page and let me know if you’re still having issues with it.

Thank you for quick reply.
Yes, indeed this is another way around it.
Going for this method.

P.S thank you and all the peeps of community for this awesome library. Keep it up :slight_smile:

1 Like

Hey @aesmail, How can I pass this conn struct to my admin in resources.

  def create_resources(conn) do
    [
         profile: [
        name: "User Actions",
        resources: [
          user: [schema: Stakester.Accounts.User, admin: Stakester.Admin.ProfileManagementAdmin, conn: conn],
        ]
      ]
    ]
  end
end

In create_resources conn is returning a struct as it should but in admin module of resource
it is showing something like
Stakester.Accounts.User.

I am unable to understand what is going on. How can I my good old conn struct back. I also tried to add conn in resources as you can see but its no use :sweat_smile:

If you need anything from my side, please let me know.

Waiting to hear back from you. :slight_smile:

Hey @kman4you, what do you need to do exactly? conn is passed to some functions but not all of them (yet). I’m assuming maybe you need to customize the fields you’re displaying based on some conn-related conditions. In this case, this is currently not supported. However, I could see how this would be useful and it would be a good feature to add.

Originally, most functions were passed the schema module (as you found out). In later versions, conn was also passed to some functions where it made sense. The general idea is, if conn is useful for a certain function, it should probably be passed as an argument.

If this is what you need, you could add it as an issue on GitHub just for reference and a reminder.
I plan on releasing v0.10.0 soon. This might be a quick fix that would make it for the v0.10.0 release.

1 Like

Hello @aesmail, thank you for the quick response.
I am trying to make current dashboard user’s management module, (as you can get the idea from example below) so he can perform some actions that he wants like changing his name or email and other stuff like that and I need to get the current_user which is in the conn -> assigns. This is what I am trying to do.

P.S I got your point that currently it is not possible, so I am adding an issue on GitHub as a reminder and waiting for the next release.

profile: [
        name: "User Actions",
        resources: [
          user: [schema: Stakester.Accounts.User, admin: Stakester.Admin.ProfileManagementAdmin, conn: conn],
        ]
      ] 

If you want to limit access to some resources (showing only resources for a specific user for example), you could actually use custom queries. You could filter the results based on current_user and show that instead of the default result (all users).

Would this work?

1 Like

Yes, for now this will work but the other solution will be better if we can just pass the conn so this process can be simplified.

1 Like

Looking great! Wanted to check this out but it appears the demo is down.

3 Likes

Yeah I’ve been meaning to fix that but things got a bit messy locally trying the demo with the unreleased v0.10.0. I’ll try to get it back up as soon as possible.

1 Like

FYI creating a new item that has a many-to-many relationship fails with

protocol Phoenix.HTML.Safe not implemented for #Ecto.Association.NotLoaded<association :xyz is not loaded> of type Ecto.Association.NotLoaded (a struct). This protocol is implemented for the following type(s): Decimal, Phoenix.LiveView.Component, Phoenix.LiveView.Rendered, Phoenix.LiveView.Comprehension, Phoenix.LiveComponent.CID, Integer, NaiveDateTime, Date, Atom, BitString, Phoenix.HTML.Form, Time, Tuple, Float, DateTime, List
1 Like