Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
fa4bbd9
added the handling for embeds_one to the InlineCRUD field
thanos Oct 1, 2025
0e1c9e3
added demo based on producst as discussed in #1583
thanos Oct 18, 2025
c73b858
resynced to develop
thanos Oct 18, 2025
6b6f4f9
fixed format
thanos Oct 18, 2025
5fe0ef7
Revert .tool-versions change
thanos Oct 20, 2025
f020f74
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
aa2c440
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
35bbcf3
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
679fae2
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
9506298
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
996016f
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
3b101cd
adding :map to Backpex.Fields.InlineCRUD and a polymorphic example to…
thanos Oct 20, 2025
f892c09
fixed lint issue
thanos Oct 20, 2025
0d96b88
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
fe9429b
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
040c699
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
a180207
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
b4feb98
Update demo/lib/demo_web/components/layouts/admin.html.heex
thanos Oct 21, 2025
df76b2a
fixed issues around changeset
thanos Oct 21, 2025
da95dca
strange issue in factory
thanos Oct 21, 2025
0577294
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
d9b5f4e
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
32732a5
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
c115c61
fixed issues around changeset
thanos Oct 21, 2025
d627096
fixed issues around changeset
thanos Oct 21, 2025
1f906ac
Update lib/backpex/fields/inline_crud.ex
thanos Oct 21, 2025
1b85afb
Update demo/lib/demo_web/live/person_live.ex
thanos Oct 22, 2025
80377ab
made copy_errors and copy_values a defp
thanos Oct 22, 2025
cde3e62
Update demo/lib/demo/ecto_factory.ex
thanos Oct 22, 2025
592dd42
Update lib/backpex/fields/inline_crud.ex
thanos Oct 22, 2025
be57953
Update demo/lib/demo_web/live/car_live.ex
thanos Oct 22, 2025
8028046
Update demo/lib/demo_web/live/person_live.ex
thanos Oct 22, 2025
aa1d255
Update lib/backpex/fields/inline_crud.ex
thanos Oct 22, 2025
14f294b
Update demo/lib/demo/ecto_factory.ex
thanos Oct 22, 2025
31870e3
Merge pull request #3 from naymspace/develop
thanos Oct 22, 2025
c1b4fc3
fixed format issues
thanos Oct 22, 2025
addec2c
fixed format issues
thanos Oct 22, 2025
68d10c3
fixed credo warnings
thanos Oct 22, 2025
5f05e45
fixed credo warnings
thanos Oct 22, 2025
7a534ae
fixed credo warnings
thanos Oct 22, 2025
ebc5599
fixed credo warnings
thanos Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
erlang 28.1
elixir 1.18.4
elixir 1.18.4-otp-28
35 changes: 35 additions & 0 deletions demo/lib/demo/ecto_factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Demo.EctoFactory do

alias Demo.Address
alias Demo.Category
alias Demo.Entity
alias Demo.FilmReview
alias Demo.Post
alias Demo.Product
Expand All @@ -13,6 +14,8 @@ defmodule Demo.EctoFactory do
alias Demo.Tag
alias Demo.User

alias Faker.Phone.EnUs, as: FakerPhone

def user_factory do
%User{
username: Faker.Internet.user_name(),
Expand Down Expand Up @@ -61,6 +64,10 @@ defmodule Demo.EctoFactory do
quantity: Enum.random(0..1_000),
manufacturer: "https://example.com/",
price: Enum.random(50..5_000_000),
more_info: %{
weight: Enum.random(1..100),
goes_well_with: Faker.Food.description()
},
suppliers: build_list(Enum.random(0..5), :supplier),
short_links: build_list(Enum.random(0..5), :short_link)
}
Expand Down Expand Up @@ -102,4 +109,32 @@ defmodule Demo.EctoFactory do
%{label: Enum.at(labels, index), url: "https://example.com/"}
end
end

def car_factory do
%Entity{
identity: Faker.Vehicle.En.make_and_model(),
type: "car",
fields: %{
"engine_size" => Enum.random([1800, 2000, 2300, 2500, 3000, 3400, 4000, 5000]),
"colour" => Faker.Color.En.name(),
"year" => Enum.random(1900..2025)
}
}
end

def person_factory do
first_name = Faker.Person.first_name()
last_name = Faker.Person.last_name()

%Entity{
identity: "#{first_name} #{last_name}",
type: "person",
fields: %{
"email" => Faker.Internet.email(),
"phone" => FakerPhone.phone(),
"age" => Enum.random(18..85),
"weight" => Enum.random(45..130)
}
}
end
end
23 changes: 23 additions & 0 deletions demo/lib/demo/entity.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule Demo.Entity do
@moduledoc false
use Ecto.Schema

import Ecto.Changeset

@primary_key {:id, :binary_id, autogenerate: true}
schema "entities" do
field :identity, :string
field :type, :string
field :fields, :map, default: %{}

timestamps()
end

@required_fields [:identity, :type, :fields]
@doc false
def changeset(address, attrs, _metadata \\ []) do
address
|> cast(attrs, @required_fields)
|> validate_required(@required_fields)
end
end
14 changes: 14 additions & 0 deletions demo/lib/demo/product.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ defmodule Demo.Product do

field :price, Money.Ecto.Amount.Type

embeds_one :more_info, MoreInfo do
field :weight, :integer
field :goes_well_with, :string
end

has_many :suppliers, Supplier, on_replace: :delete, on_delete: :delete_all
has_many :short_links, ShortLink, on_replace: :delete, on_delete: :delete_all, foreign_key: :product_id

Expand All @@ -29,6 +34,9 @@ defmodule Demo.Product do
def changeset(product, attrs, _metadata \\ []) do
product
|> cast(attrs, @required_fields ++ @optional_fields)
|> cast_embed(:more_info,
with: &more_info_changeset/2
)
|> cast_assoc(:suppliers,
with: &Demo.Supplier.changeset/2,
sort_param: :suppliers_order,
Expand All @@ -42,4 +50,10 @@ defmodule Demo.Product do
|> validate_required(@required_fields)
|> validate_length(:images, max: 2)
end

def more_info_changeset(more_info, attrs) do
more_info
|> cast(attrs, [:weight, :goes_well_with])
|> validate_required([:weight])
end
end
13 changes: 13 additions & 0 deletions demo/lib/demo_web/components/layouts/admin.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,20 @@
<Backpex.HTML.CoreComponents.icon name="hero-book-open" class="size-5" /> Tickets
</Backpex.HTML.Layout.sidebar_item>
</Backpex.HTML.Layout.sidebar_section>
<Backpex.HTML.Layout.sidebar_section id="entities">
<:label>Polymorphic Example</:label>
<Backpex.HTML.Layout.sidebar_item current_url={@current_url} navigate="/admin/entities">
<Backpex.HTML.CoreComponents.icon name="hero-book-open" class="size-5" /> Entities
</Backpex.HTML.Layout.sidebar_item>
<Backpex.HTML.Layout.sidebar_item current_url={@current_url} navigate="/admin/persons">
<Backpex.HTML.CoreComponents.icon name="hero-book-open" class="size-5" /> Persons
</Backpex.HTML.Layout.sidebar_item>
<Backpex.HTML.Layout.sidebar_item current_url={@current_url} navigate="/admin/cars">
<Backpex.HTML.CoreComponents.icon name="hero-book-open" class="size-5" /> Cars
</Backpex.HTML.Layout.sidebar_item>
</Backpex.HTML.Layout.sidebar_section>
</:sidebar>

<Backpex.HTML.Layout.flash_messages flash={@flash} />
{render_slot(@inner_block)}
</Backpex.HTML.Layout.app_shell>
92 changes: 92 additions & 0 deletions demo/lib/demo_web/live/car_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
defmodule DemoWeb.CarLive do
@moduledoc """
This module is similar to `PersonLive` in that it demos the use of a InlineCRUD set for type `:map`.
It also demos how you can use the power of `Backpex` to do a poor man's polymorphism by using `Backpex.LiveResource`
to control the values entered into the generic `:map` field while reusing the same schema
for different entity types.

It also is an example of how you could validate the values in the `:map` field by using a `validate`
function and calling `Backpex.Fields.InlineCRUD.changeset` inside the `changeset` function.
"""
use Backpex.LiveResource,
adapter_config: [
schema: Demo.Entity,
repo: Demo.Repo,
update_changeset: &__MODULE__.changeset/3,
create_changeset: &__MODULE__.changeset/3,
item_query: &__MODULE__.item_query/3
],
layout: {DemoWeb.Layouts, :admin},
fluid?: true

import Ecto.Query, only: [where: 3]

alias Backpex.Fields.InlineCRUD
alias Demo.Entity

@impl Backpex.LiveResource
def singular_name, do: "Car"

@impl Backpex.LiveResource
def plural_name, do: "Cars"

#
# Added the item_query function to filter entities by type
#
def item_query(query, _view, _assigns) do
query
|> where([entity], entity.type == "car")
end

#
# Added the changeset function to create a new entity with type "car",
# and validate the child fields in the entity
#
def changeset(entity, params, metadata \\ []) do
entity
|> Entity.changeset(params |> Map.put("type", "car"))
|> InlineCRUD.changeset(:fields, metadata)
end

@impl Backpex.LiveResource
def fields do
[
identity: %{
module: Backpex.Fields.Text,
label: "Model",
searchable: true
},
fields: %{
module: Backpex.Fields.InlineCRUD,
label: "Information",
type: :map,
except: [:index],
child_fields: [
engine_size: %{
module: Backpex.Fields.Text,
label: "Engine Size (cc)",
input_type: :integer
},
colour: %{
module: Backpex.Fields.Text,
label: "Colour"
},
year: %{
module: Backpex.Fields.Text,
label: "Year",
input_type: :integer
}
],
validate: fn changeset ->
changeset
|> Ecto.Changeset.validate_required([:colour, :year])
|> Ecto.Changeset.validate_number(:year,
greater_than: 1900,
less_than: Date.utc_today().year + 1,
message: "must be between 1900 and #{Date.utc_today().year}"
)
end
}
]
end
end
54 changes: 54 additions & 0 deletions demo/lib/demo_web/live/entity_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
defmodule DemoWeb.EntityLive do
use Backpex.LiveResource,
adapter_config: [
schema: Demo.Entity,
repo: Demo.Repo,
update_changeset: &Demo.Entity.changeset/3,
create_changeset: &Demo.Entity.changeset/3
],
layout: {DemoWeb.Layouts, :admin},
fluid?: true

@impl Backpex.LiveResource
def singular_name, do: "Entity"

@impl Backpex.LiveResource
def plural_name, do: "Entities"

@impl Backpex.LiveResource
def item_actions(default_actions) do
default_actions
|> Keyword.delete(:delete)
end

@impl Backpex.LiveResource
def can?(_assigns, :index, _item), do: true

@impl Backpex.LiveResource
def can?(_assigns, _action, _item), do: false

@impl Backpex.LiveResource
def fields do
[
identity: %{
module: Backpex.Fields.Text,
label: "Identity",
searchable: true
},
type: %{
module: Backpex.Fields.Text,
label: "Type",
searchable: true,
readonly: true
},
fields: %{
module: Backpex.Fields.Textarea,
rows: 10,
label: "Fields",
searchable: true,
readonly: true,
except: [:index]
}
]
end
end
79 changes: 79 additions & 0 deletions demo/lib/demo_web/live/person_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
defmodule DemoWeb.PersonLive do
@moduledoc """
This module just demos the use of a InlineCRUD set for type `:map`.
It also demos how you can use the power of `Backpex` to do a poor man's polymorphism by using `Backpex.LiveResource`
to control the values entered into the generic `:map` field while reusing the same schema
for different entity types.
"""
use Backpex.LiveResource,
adapter_config: [
schema: Demo.Entity,
repo: Demo.Repo,
update_changeset: &__MODULE__.changeset/3,
create_changeset: &__MODULE__.changeset/3,
item_query: &__MODULE__.item_query/3
],
layout: {DemoWeb.Layouts, :admin},
fluid?: true

import Ecto.Query, only: [where: 3]

alias Demo.Entity

@impl Backpex.LiveResource
def singular_name, do: "Person"

@impl Backpex.LiveResource
def plural_name, do: "Persons"

#
# Added the item_query function to filter entities by type
#
def item_query(query, _view, _assigns) do
query
|> where([entity], entity.type == "person")
end

#
# Added the changeset function to create a changeset for a person entity
#
def changeset(entity, params, _metadata \\ []) do
entity
|> Entity.changeset(params |> Map.put("type", "person"))
end

@impl Backpex.LiveResource
def fields do
[
identity: %{
module: Backpex.Fields.Text,
label: "Name",
searchable: true
},
fields: %{
module: Backpex.Fields.InlineCRUD,
label: "Information",
type: :map,
except: [:index],
child_fields: [
email: %{
module: Backpex.Fields.Text,
label: "Email"
},
phone: %{
module: Backpex.Fields.Text,
label: "Phone"
},
age: %{
module: Backpex.Fields.Text,
label: "Age"
},
weight: %{
module: Backpex.Fields.Text,
label: "Weight (kg)"
}
]
}
]
end
end
18 changes: 18 additions & 0 deletions demo/lib/demo_web/live/product_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ defmodule DemoWeb.ProductLive do
label: "Price",
align: :right
},
more_info: %{
module: Backpex.Fields.InlineCRUD,
label: "More Info",
type: :embed_one,
except: [:index],
child_fields: [
weight: %{
module: Backpex.Fields.Text,
label: "Ave. Weight (kg)"
},
goes_well_with: %{
module: Backpex.Fields.Textarea,
label: "Goes well with",
input_type: :textarea,
rows: 5
}
]
},
suppliers: %{
module: Backpex.Fields.InlineCRUD,
label: "Suppliers",
Expand Down
Loading
Loading