diff --git a/demo/lib/demo/user.ex b/demo/lib/demo/user.ex index d8b9bd860..afb0daeea 100644 --- a/demo/lib/demo/user.ex +++ b/demo/lib/demo/user.ex @@ -15,7 +15,7 @@ defmodule Demo.User do field :full_name, :string, virtual: true field :age, :integer field :role, Ecto.Enum, values: [:user, :admin] - field :permissions, {:array, :string} + field :permissions, {:array, :string}, default: [] field :avatar, :string field :deleted_at, :utc_datetime diff --git a/demo/lib/demo_web/live/user_live.ex b/demo/lib/demo_web/live/user_live.ex index 711abe0f6..4f30facd9 100644 --- a/demo/lib/demo_web/live/user_live.ex +++ b/demo/lib/demo_web/live/user_live.ex @@ -120,6 +120,16 @@ defmodule DemoWeb.UserLive do options: [Admin: "admin", User: "user"], prompt: "Choose role..." }, + permissions: %{ + module: Backpex.Fields.Checkgroup, + label: "Permissions", + options: [ + {"Create Posts", "create_posts"}, + {"Edit Posts", "edit_posts"}, + {"Delete Posts", "delete_posts"}, + {"Manage Users", "manage_users"} + ] + }, posts: %{ module: Backpex.Fields.HasMany, label: "Posts", @@ -193,15 +203,6 @@ defmodule DemoWeb.UserLive do label: "Notes" } ] - }, - permissions: %{ - module: Backpex.Fields.MultiSelect, - label: "Permissions", - options: [ - {"Can access admin panel", "can_access_admin_panel"}, - {"Item actions", [{"Delete", "delete"}, {"Edit", "edit"}, {"Show", "show"}]}, - {"Other actions", [{"Can send email", "can_send_email"}]} - ] } ] end diff --git a/demo/priv/repo/migrations/20251205230743_set_default_for_permissions.exs b/demo/priv/repo/migrations/20251205230743_set_default_for_permissions.exs new file mode 100644 index 000000000..401a9023f --- /dev/null +++ b/demo/priv/repo/migrations/20251205230743_set_default_for_permissions.exs @@ -0,0 +1,15 @@ +defmodule Demo.Repo.Migrations.SetDefaultForPermissions do + use Ecto.Migration + + def up do + # Set default for new records + execute "ALTER TABLE users ALTER COLUMN permissions SET DEFAULT '{}'::text[]" + + # Update existing NULL values to empty array + execute "UPDATE users SET permissions = '{}' WHERE permissions IS NULL" + end + + def down do + execute "ALTER TABLE users ALTER COLUMN permissions DROP DEFAULT" + end +end diff --git a/lib/backpex/fields/checkgroup.ex b/lib/backpex/fields/checkgroup.ex new file mode 100644 index 000000000..1e959b115 --- /dev/null +++ b/lib/backpex/fields/checkgroup.ex @@ -0,0 +1,91 @@ +defmodule Backpex.Fields.Checkgroup do + @config_schema [ + options: [ + doc: "List of options or function that receives the assigns.", + type: {:or, [{:list, :any}, {:fun, 1}]}, + required: true + ] + ] + + @moduledoc """ + A field for handling multiple checkboxes with predefined options. + + This field stores selected values as an array. + + ## Field-specific options + + See `Backpex.Field` for general field options. + + #{NimbleOptions.docs(@config_schema)} + + ## Example + + @impl Backpex.LiveResource + def fields do + [ + roles: %{ + module: Backpex.Fields.Checkgroup, + label: "Roles", + options: [{"Admin", "admin"}, {"User", "user"}, {"Editor", "editor"}] + } + ] + end + """ + use Backpex.Field, config_schema: @config_schema + + alias Backpex.HTML + + @impl Backpex.Field + def render_value(assigns) do + options = get_options(assigns) + labels = get_labels(assigns.value, options) + + assigns = assign(assigns, :labels, labels) + + ~H""" +
+ {if @labels == [], do: raw("—"), else: @labels |> Enum.map(&HTML.pretty_value/1) |> Enum.join(", ")} +
+ """ + end + + @impl Backpex.Field + def render_form(assigns) do + options = get_options(assigns) + + assigns = assign(assigns, :options, options) + + ~H""" +