Giter Site home page Giter Site logo

backoffice's People

Contributors

edisonywh avatar nduatik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

backoffice's Issues

LiveComponent support for rendering form fields

Rendering the LiveComponent isn't the hard part, but the hard part is in figuring out how to handle_events.

The markup would look like this:

Index.Live > FormComponent > CustomField

But users don't really get access to the FormComponent itself, so maybe we can define a generic fallback handle_event for FormComponent like so (pseudo code):

def handle_event({:update, field, value}) do
   # somehow update the current changeset and validate it (and re-render so the child component gets new values/validation error)
end

This means that maybe we can have a bit more of a complex logic in our CustomField component, then just send over a normalized version that can be popped into the changeset.

For example, I have a Listing form that can belong to a single Newsletter. I would love to be able to have a typeahead & suggestion box for Newsletters while I'm typing, and then when I click on the value, I want it to store it to the newsletter_id field in the Listing changeset instead.

Not sure how feasible this is


EDIT: It doesn't really seem possible because phx-change works on a form level, and we can't nest form in HTML (correct me if I'm wrong).

One way to do this is maybe via JS hooks?

Render different forms?

zachdaniel from Ash pointed out one feature he has in ash_admin is the ability to have multiple forms. This could be a pretty cool idea, I can imagine the declaration to read something like:

form :create do
  ...
end

form :edit do
 ...
end

form :custom do
  ...
end

Questions

  • How do we instruct FormComponent which form to render?
  • How should it work with resolvers? Perhaps the resolver_opts for Ecto plugin can use specific changeset for specific forms

Allow users to bring their own Hooks

Haven't tested it but I don't think this is possible right now. We allow users to pass in their own app.js, but then Backoffice initiates its own LiveSocket, so I'm not sure how to pass in user-created hooks into the LiveSocket instance

Default ordering in :index

Hi,

I am using MSSQL for my project and whenever I use the index action, it throws the following error

ORDER BY is mandatory when OFFSET is set in query:

from m0 in MyApp.MemberUser,
  limit: ^...,
  offset: ^...,
  select: m0,
  preload: [[]]

This is caused by MSSQL's requirement that an ordering column be provided. I have solved this by adding |> order_by(:id) to the entries function in your Ecto Resolver.

  defp entries(query, repo, page_number, _, page_size) do
    offset = page_size * (page_number - 1)

    query
    |> offset(^offset)
    |> limit(^page_size)
    |> all(repo)
  end

becomes

  defp entries(query, repo, page_number, _, page_size) do
    offset = page_size * (page_number - 1)

    query
    |> offset(^offset)
    |> limit(^page_size)
    |> order_by(:id)
    |> all(repo)
  end

Would it possible to make this the default behaviour to support MSSQL or to provide a way of defining the order in the use Backoffice.Resource.Index options

How to handle user-land CSS/JS? (Tailwind purging)

I disabled purging for Tailwind, because there was some issue that's preventing the form fields to render correctly.

Also, if we disable it then it means that custom rendering from userland won't have access to Tailwind (or at least only a subset that's not purged yet).

Backoffice uses the backoffice.html layout file, but then that only references Backoffice's app.css, not css files from userland, so this means that we might need to provide a way for user to supply their custom css & js files, and then we can proceed to purge unused Tailwind classes.

The idea is probably for user to supply it in the layout page, just as they do with logo & links now.

Remove dependency on Ecto.Changeset?

Right now we rely on Ecto.Changeset for forms, we can look into it further and see if we need to do that, or if there's some possibility to move away from Ecto.Changset and implement Phoenix.HTML.FormData ourselves

Reference (don't inject) app.js and app.css in layout

A few days ago, when we fixed purging I noticed a huge increase in the page load speeds. Turns out we had been sending 3mb in each request had a huge effect - even on localhost. Once we trimmed that to 22KB, the page loads became blazing fast (relatively at least).

As a result, I have been thinking about how else we could optimize Backoffice page loads.

Looking at Kaffy's app.html.eex it seems like they are referencing static files instead of injecting them as raw text into the base layout. This would allow caching of the css and js on the browser.

I think this would work perfectly in our case. Taking the example of one of my pages, the page loads at 193KB. Interestingly, only 42KB is the html. The other 151KB is static css and js. Reducing page sizes by nearly 80% in some cases seems worth pursuing.

Textarea rendering

Hi,
I've been using backoffice to edit text in a blog-like application. In most cases, we expect multiple sentences and perhaps multiple paragraphs.

To make editing easy, I have been using the :textarea type on my form field. This however produces json encoded strings.

image

Not pretty or easy to use.

This seems to be caused by the Phoenix.json_library().encode applied to the text here

Removing the json encoding seems to resolve the issue

image

Datetime picker

Hi @edisonywh,

Are there any plans implement a datetime picker for Ecto date and time types (:utc_datetime, :naive_datetime, :date, :time, :any, :utc_datetime_usec, :naive_datetime_usec, :time_usec)?

According to Mozilla's browser documentation, text=[datetime-local] is not fully supported.. This makes me think we would need a cross browser (read custom) implementation for a consistent experience.

What do you think of using an existing tailwind implementation? Here are some I found:

Does it even have to be a tailwind datepicker?

I think we can ignore the lack of time pickers as most people will have no trouble typing them in (default to the current time?) but will benefit from not having to type in dates using the ISO format.

Things become tricky when we have to handle UTC vs local timezone so maybe placing a dropdown with UTC vs local might be of value.


What do you think?

Complex filtering UI

Would be nice to support a more complex filtering system with a nice UI. For example, given an index/1 that looks like:

index do
  field :name, :string, search: true
  field :age, :integer, search: true
  field :verified, :boolean, search: true
end

We know the type of the field, so we should be able to render the corresponding form fields for them (text_input for :string, number_input for :age, checkbox for :boolean). Then on field submission we can package it into a nice query params and have UserLive.Index (Backoffice.Resources) handle the query params, before passing it off to Resolvers.

e.g:

`?name=search&age=>18&verified=true&page=1" then perhaps convert it to something more structured for resolvers to work with?

Then the resolvers receives them in page_otps and can decide what to do with these fields.

RFC: DSL?

Right now users configure pages with just functions (as callbacks to behaviour), but do we want to continue to do that, or go with a DSL approach that looks similar to Ecto? I generally prefer to keep things as functions and stray away from DSL as much as possible, but I can see a couple of benefits here. I'd love to hear arguments for both side.

Pros

With DSL, we:

  1. don't need to have a weird id: nil just to display data. I find it pretty strange to pass in nil just to display something.
  2. we can most likely do some compile time check to make sure the types are valid (and hence able to be rendered into form fields or something), for example:
def MyWeb.UserLive.Index do
  use Backoffice.Resources, ...

  index do
    field :id, :string
    field :age, :integer, label: "User Age"
  end

  form do
    field :id, :string
    field :age, :integer, label: "User Age"
    field :what, :non_existent_type
    
    belongs_to :team, Team
  end
end

We map types into form fields, so if we don't know how to render :non_existent_type, we can error out during compilation.

With DSL we can also maybe simplify the set-up, so we don't need to repeat things like "search_fields/1", and instead:

index do
  field :id, :string # defaults to searchable
  field :age, :string, search: true,
  field :private, :string, search: false
  field :verified, :boolean, filter: true
end

You can define everything in one DSL itself.

The filter field, along with the type field, most likely doesn't matter in index, but only in form, but when used in index, it can help us build out a more complex FilterComponent (multiple filters, and we know what fields to render.

Questions

  • How do we allow custom rendering? Currently it's:
def index do
  [
    custom: %{label: "Custom", value: fn resource -> ... end}
  ]
end

But if we go DSL which does it on compile time, how can we allow user to customize the field with a function? Can we capture a function during compile time, or can we do via MFA?

Encourage Context functions instead?

This might result in a different API, but right now Backoffice does not use your context functions, but rather changesets directly. It is possible for you to use different changesets for different forms, but then this tightly couples to your changesets, and also would it be a better idea to allow users to pipe things into their context functions instead?

Turn table view into a widget

Not sure if it's possible since we need some sort of state for table (sorted etc), but we can give it a try and see how it goes.

It might be pretty cool to build everything as a widget, and then by having a TableWidget we can also reuse it when editing associations etc.

Try out Surface?

Surface seems pretty interesting, I wouldn't mind trying it out but I'm not sure if I want to include another dependency for Backoffice. There are also some things we need to figure out:

  • can you render a normal LiveComponent within Surface? One thing we want to try to do is allow users to pass in a LiveComponent as a form field, would it still be possible if we use Surface?

I'd love to hear more pros & cons if you have any!

Filter UI appearance

Looking at pull request #41, I expect the UI below.

image

However, I am instead getting:

image

The padding and alignments are off. @edisonywh, are you providing some styling locally that are not in the library?

Theming support for Tailwind

With Tailwind and CSS properties we can easily theme the app.

See video: https://youtu.be/MAtaT8BZEAo

The idea is for Backoffice to ship a light and dark theme, and then user can also supply their own theme. Ideally all they need to do is define css properties and then include the CSS files

Add Home/Dashboard page support?

At the moment there is Backoffice has no clear home/dashboard page. I think this could be a nice value addition. Just a simple page that has the app logo, some widgets with useful stats and some shortcuts to existing resources. @edisonywh, what do you think? Is this a stretch for the library, something outside scope?

What I have in mind is something that looks like:
PotentialHomePage

At a high-level, we would add a new page type. Maybe Backoffice.Page.Dashboard?
That page would have two functions: widgets and shortcuts.

widgets would return a list of widgets, just like those at in the Index page.

def widgets(socket) do
    %Backoffice.PlainWidget{
      title: "Blog Posts",
      data: ...
    },
    %Backoffice.PlainWidget{
      title: "Podcast Episodes",
      data:  ...
    },
    %Backoffice.PlainWidget{
      title: "Unique Visitors",
      data:  ...
    }
end

shortcuts would return a list of shortcuts to other pages.

def shortcuts do
[
    %Backoffice.ShortcutWidget{
      title: "Blog",
      subtitle: "Get writing",
      color: "#23A342",
      icon: #svg_data
    }
]

New resource url params

Do you think it would be useful to allow links to the new resource page to accept parameters that could allow loading of values into the changeset?


For example, lets say I was dealing with university courses and wanted to add a new class/unit to an existing course.
The ideal experience would be to go to the course I want to change and click on an Add class.
This would load the url /class/new?course_id=1 and open the create form with the course already provided.

Better animations + notifications

We have Tailwind & Alpine so we should utilize them to have better animations (animating side menus, animating form coming into view, notifications popping up etc).

We should also handle flash messages and display them on the top right corner or something

What's the impact of having two LiveView on the same page?

I came across an issue where I include my app's app.js in Backoffice, which sets up LiveSocket, but the problem is Backoffice already sets up an instance of LiveSocket. I didn't dive too much into what the implications are but from what I saw it was causing handle_event being fired twice.

We need to further investigate this and document it, perhaps suggesting a workaround.

This also tie into how we allow users to supply user defined Hooks to our Backoffice LiveSocket

Project status?

I've been searching "internal admin tool/dashboard" libraries like this and Kaffy, but I noticed that the last commit to this project was over two years ago. Is this project still being worked on?

I acknowledge this is an open-source project and there is zero obligation for anyone to keep it up-to-date. I was just curious. Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.