### Fetch Dependencies
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
After adding the dependency to mix.exs, run this command to fetch and install it.
```bash
mix deps.get
```
--------------------------------
### Generator Command Examples
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Examples of using the `mix ash_form_builder.gen.live` task to generate LiveView code for different resources and output directories.
```bash
mix ash_form_builder.gen.live Inventory Product --page-size 50
mix ash_form_builder.gen.live Accounts User --out lib/my_app_web/live/admin
```
--------------------------------
### File Metadata Capture Example
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Shows an example of capturing file metadata such as path, filename, size, and content type.
```elixir
metadata = %{
path: stored.location.path,
filename: entry.client_name,
size: entry.size, # File size in bytes
content_type: entry.client_type, # MIME type
uploaded_at: DateTime.utc_now() |> DateTime.to_iso8601()
}
```
--------------------------------
### Configure Documentation Generation in mix.exs
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/DOCUMENTATION.md
Configure the `docs` function in `mix.exs` to specify main documentation, source references, and include extra files like guides and examples.
```elixir
defp docs do
[
main: "readme",
source_ref: "v#{project()[:version]}",
source_url: project()[:source_url],
extras: [
"README.md",
"CHANGELOG.md",
"guides/todo_app_integration.exs",
"guides/relationships_guide.exs",
"example_usage.ex"
],
groups_for_extras: [
Guides: ["guides/todo_app_integration.exs", "guides/relationships_guide.exs"],
Examples: ["example_usage.ex"]
],
groups_for_modules: [
"Core Modules": [...],
Themes: [...],
Transformers: []
]
]
end
```
--------------------------------
### Custom Theme Implementation
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Implement the AshFormBuilder.Theme behaviour to create a custom theme. This example shows how to render a text input and a combobox.
```elixir
defmodule MyAppWeb.CustomTheme do
@behaviour AshFormBuilder.Theme
use Phoenix.Component
@impl AshFormBuilder.Theme
def render_field(assigns, opts) do
case assigns.field.type do
:text_input -> render_text_input(assigns)
:multiselect_combobox -> render_combobox(assigns)
# ... etc
end
end
defp render_text_input(assigns) do
~H"""
"""
end
end
```
--------------------------------
### Theme Customization Guide Topics
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Lists the main topics covered in the theme customization guide, including extending themes, wrapper patterns, and framework integration.
```elixir
- Extend Default Theme
- Wrapper Component Pattern
- CSS Framework Integration (Bootstrap example)
- Accessibility-Focused Theme
- RTL Language Support
```
--------------------------------
### Complete Ash Resource with File Upload Fields
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
An example Ash resource demonstrating various file upload configurations, including single file uploads with previews, custom target attributes, and multiple file uploads. It specifies constraints like max entries, file size, and accepted file types.
```elixir
defmodule MyApp.Projects.Project do
use Ash.Resource,
domain: MyApp.Projects,
extensions: [AshFormBuilder]
attributes do
uuid_primary_key :id
attribute :name, :string, allow_nil?: false
attribute :proposal_path, :string
attribute :contract_path, :string
attribute :attachments, {:array, :string}, default: []
end
actions do
create :create do
accept [:name]
argument :proposal, :string, allow_nil?: true
argument :contract, :string, allow_nil?: true
argument :attachments, {:array, :string}, allow_nil?: true
end
update :update do
accept [:name]
argument :proposal, :string, allow_nil?: true
argument :contract, :string, allow_nil?: true
argument :attachments, {:array, :string}, allow_nil?: true
end
end
form do
action :create
submit_label "Create Project"
# Single file with image preview
field :proposal do
type :file_upload
label "Project Proposal"
hint "PDF or Word document (max 10 MB)"
opts upload: [
cloud: MyApp.Projects.Cloud,
max_entries: 1,
max_file_size: 10_000_000,
accept: ~w(.pdf .doc .docx)
]
end
# Single file with custom target
field :contract do
type :file_upload
label "Signed Contract"
hint "Upload signed contract"
opts upload: [
cloud: MyApp.Projects.Cloud,
max_entries: 1,
max_file_size: 10_000_000,
accept: ~w(.pdf .jpg .jpeg .png),
target_attribute: :contract_path # Explicit mapping
]
end
# Multiple files
field :attachments do
type :file_upload
label "Additional Attachments"
hint "Upload up to 5 files"
opts upload: [
cloud: MyApp.Projects.Cloud,
max_entries: 5,
max_file_size: 10_000_000,
accept: ~w(.pdf .doc .docx .xls .xlsx)
]
end
end
end
```
--------------------------------
### Basic File Upload Setup
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
Defines a basic file upload field for a user's avatar. The file path is automatically stored in the `avatar_path` attribute without needing manual functions.
```elixir
defmodule MyApp.Users.User do
use Ash.Resource,
domain: MyApp.Users,
extensions: [AshFormBuilder]
attributes do
uuid_primary_key :id
attribute :name, :string, allow_nil?: false
attribute :avatar_path, :string # ← Auto-detected target
end
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
# No manual change function needed!
end
end
form do
action :create
field :avatar do
type :file_upload
label "Profile Photo"
opts upload: [
cloud: MyApp.Buckets.Cloud,
max_entries: 1,
max_file_size: 5_000_000,
accept: ~w(.jpg .jpeg .png)
]
end
end
end
```
--------------------------------
### RTL Language Support Example
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/guides/theme_customization_guide.md
This snippet demonstrates how to add support for Right-to-Left (RTL) languages by setting the 'dir' attribute. Configure in config.exs with `theme_opts: [dir: "rtl"]`.
```elixir
defp render_text_input(assigns) do
dir = Keyword.get(@theme_opts, :dir, "ltr")
~H"""
"""
end
# Configure in config.exs:
# config :ash_form_builder, theme_opts: [dir: "rtl"]
```
--------------------------------
### Using Custom Theme in LiveView
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/guides/theme_customization_guide.md
Integrate your custom form theme into a LiveView component. This example shows how to mount a form and render it using `AshFormBuilder.FormComponent`.
```elixir
defmodule MyAppWeb.ClinicLive.Form do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
form = MyApp.Billing.Clinic.Form.for_create(actor: socket.assigns.current_user)
{:ok, assign(socket, form: form)}
end
@impl true
def render(assigns) do
~H"""
<.live_component
module={AshFormBuilder.FormComponent}
id="clinic-form"
resource={MyApp.Billing.Clinic}
form={@form}
/>
"""
end
end
```
--------------------------------
### Migration: Manual to Automatic File Attribute Handling
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
Illustrates the migration from manually handling file uploads using `change` functions in Ash actions to the automatic attribute mapping provided by Ash Form Builder. The 'After' example shows how the `argument` definition is sufficient.
```elixir
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
change fn changeset, _ ->
case Ash.Changeset.get_argument(changeset, :avatar) do
nil -> changeset
path -> Ash.Changeset.change_attribute(changeset, :avatar_path, path)
end
end
end
end
```
```elixir
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
# That's it! No helper function needed.
end
end
```
--------------------------------
### Customizing Columns in HEEx Template
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
This example shows how to replace placeholder column definitions in the `index.html.heex` template with actual resource attributes like name, email, role, and insertion time, including filtering and sorting options.
```heex
<:col :let={user} field="name" filter sort>{user.name}
<:col :let={user} field="email" filter>{user.email}
<:col :let={user} field="role" filter={:select}>{user.role}
<:col :let={user} field="inserted_at" sort>{user.inserted_at}
```
--------------------------------
### S3 Bucket Policy Example
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
An example of an S3 bucket policy that restricts access to a specific AWS account role, allowing only your application to perform actions on the bucket and its objects.
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT_ID:role/your-app-role"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::your-bucket",
"arn:aws:s3:::your-bucket/*"
]
}
]
}
```
--------------------------------
### Cross-Field Validation Example
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/VALIDATION_AND_ERRORS.md
Defines a validation rule to ensure the end date is after the start date within a form update action.
```elixir
actions do
update :update do
accept [:start_date, :end_date]
# Validate end_date is after start_date
validate {MyApp.Validations, :date_range,
fields: [:start_date, :end_date]}
end
end
```
--------------------------------
### Render Text Input with Errors
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/VALIDATION_AND_ERRORS.md
Example of how a theme component (`render_text_input`) uses `extract_field_errors` to get errors and passes them to a text input component for display.
```elixir
defp render_text_input(assigns) do
assigns = Map.put(assigns, :field_errors, extract_field_errors(assigns.form, assigns.field.name))
~H"""
<.text_field
field={@form[@field.name]}
label={@field.label}
errors={@field_errors} ← Passed to component
...
/>
"""
```
--------------------------------
### Clone Repository and Run Tests
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Provides the necessary commands to set up the development environment by cloning the repository, fetching dependencies, and running tests.
```bash
git clone https://github.com/nagieeb0/ash_form_builder.git
cd ash_form_builder
mix deps.get
mix test
```
--------------------------------
### Quick Publish to Hex.pm
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
Navigate to the project directory and execute the mix hex.publish command for a quick, interactive publication process.
```bash
cd /home/nagieeb/projects/ash_form_builder
mix hex.publish
```
--------------------------------
### Generate Documentation
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
Generate project documentation using the mix docs command.
```bash
mix docs
```
--------------------------------
### Fetch and Compile Dependencies
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
Ensure all project dependencies are fetched and compiled by running 'mix deps.get' followed by 'mix deps.compile'.
```bash
mix deps.get
mix deps.compile
```
--------------------------------
### Update Form with Existing File Preview
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
Demonstrates how to set up an update form that automatically previews existing files. It uses `for_update` to load the user and initializes the form component.
```elixir
defmodule MyAppWeb.UserLive.Edit do
use MyAppWeb, :live_view
def mount(%{"id" => id}, _session, socket) do
user = MyApp.Users.get_user!(id, load: [])
# for_update auto-loads existing avatar_path
form = MyApp.Users.User.Form.for_update(user,
actor: socket.assigns.current_user
)
{:ok, assign(socket, form: form, user: user)}
end
def render(assigns) do
~H"""
<.live_component
module={AshFormBuilder.FormComponent}
id="user-form"
resource={MyApp.Users.User}
form={@form}
/>
"""
end
end
```
--------------------------------
### Generate Full LiveView CRUD Interface
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Use this command to scaffold a complete CRUD interface for a given resource. Options allow customization of page size and output directory.
```bash
mix ash_form_builder.gen.live Accounts User
mix ash_form_builder.gen.live Blog Post --page-size 50
mix ash_form_builder.gen.live Inventory Product --out lib/my_app_web/live/admin
```
--------------------------------
### Form Level Ignore Fields Option
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Example of using the form-level `:ignore_fields` option to exclude multiple fields from the entire form.
```elixir
form MyResource, ignore_fields: [:id, :inserted_at, :updated_at] do
# ... form fields
end
```
--------------------------------
### Generate and View Documentation Locally
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/DOCUMENTATION.md
Commands to generate Elixir documentation locally using `mix docs` and view it in a browser.
```bash
# Generate docs
mix docs
# View in browser
open doc/index.html
# Generate with custom output
mix docs --output ./custom-docs
```
--------------------------------
### Configure Default Theme
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Set the default theme for Ash Form Builder by configuring the `:ash_form_builder` application. This example shows how to set the `Default` theme.
```elixir
config :ash_form_builder, :theme, AshFormBuilder.Themes.Default
```
--------------------------------
### Organize by Resource Type
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Sets up upload options to organize files into different buckets based on resource type, such as user avatars or project documents. This promotes a structured storage approach.
```elixir
# For User avatars
opts upload: [
cloud: MyApp.Cloud,
bucket_name: "users/avatars"
]
```
```elixir
# For Project documents
opts upload: [
cloud: MyApp.Cloud,
bucket_name: "projects/documents"
]
```
--------------------------------
### Field DSL Ignore Option
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Example of using the `:ignore` option within the field DSL to exclude a specific field from the form without needing a full block.
```elixir
form MyResource do
field :email, ignore: true
end
```
--------------------------------
### Configure Form Options
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
When generating forms or live components, you can specify theme, accent, and transitions using flags.
```bash
mix ash_form.gen.live -r MyApp.Context.Resource --theme MyApp.Themes.Light --accent :blue --transitions MyApp.Transitions.Slide
```
--------------------------------
### Production Environment Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Sets up cloud storage configuration for the production environment, typically using S3 and retrieving the bucket name and region from environment variables.
```elixir
# config/prod.exs
import Config
config :my_app, MyApp.Buckets.Cloud,
bucket: System.get_env("S3_BUCKET", "my-app-prod"),
region: System.get_env("AWS_REGION", "us-east-1")
```
--------------------------------
### File Upload Server-Side Validation
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/VALIDATION_AND_ERRORS.md
Example of server-side validation for file uploads within an Ash resource's actions. This `validate present` ensures that the `avatar` argument is provided during the create action.
```elixir
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
# Custom validation for file uploads
validate present([:avatar_path], at_least: 1)
end
end
```
--------------------------------
### Authenticate Hex User
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
If authentication fails, create a hex.pm account, generate an API key, and run 'mix hex.user auth' to authenticate.
```bash
mix hex.user auth
```
--------------------------------
### Volume Adapter Configuration (Local)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure the local file system adapter for development or small applications. Specify the bucket path relative to the app root and a base URL for access.
```elixir
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "priv/uploads", # Relative to your app root
base_url: "http://localhost:4000/uploads"
```
--------------------------------
### Audit Dependencies
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
Run 'mix hex.audit' to check for dependency resolution issues.
```bash
mix hex.audit
```
--------------------------------
### Detailed Theme Options Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/guides/theme_customization_guide.md
Configure a comprehensive set of theme options in `config/config.exs` for fine-grained control over form element styling. This includes classes for wrappers, labels, inputs, errors, and hints.
```elixir
# config/config.exs
config :ash_form_builder,
theme: MyAppWeb.FormBuilder.TailwindTheme,
theme_opts: [
# Global wrapper class
wrapper_class: "space-y-6",
# Field wrapper class
field_wrapper_class: "mb-4",
# Label class
label_class: "block text-sm font-medium mb-1",
# Input class (applied to all inputs)
input_class: "w-full px-3 py-2 border rounded-md",
# Error class
error_class: "text-sm text-red-600 mt-1",
# Hint class
hint_class: "text-xs text-gray-500 mt-1",
# Custom options for your theme
custom_option: "value"
]
```
--------------------------------
### Verify Package Publication
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
After publication, use the mix hex.info command to verify that the package is visible on hex.pm.
```bash
mix hex.info ash_form_builder
```
--------------------------------
### Generate Live Component for Resource
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Generates a Live Component for a given Ash resource. Use the --resource flag to specify the resource.
```bash
mix ash_form.gen.live -r MyApp.Context.Resource
```
--------------------------------
### Multi-Tenant Storage - Dynamic Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Dynamically configure storage settings per tenant at runtime. This allows each tenant to have its own bucket or specific S3 credentials.
```elixir
# In your LiveView or controller
tenant_config = [
adapter: Buckets.Adapters.S3,
bucket: "tenant-#{tenant.id}-bucket",
access_key_id: tenant.s3_key,
secret_access_key: tenant.s3_secret
]
MyApp.Buckets.Cloud.put_dynamic_config(tenant_config)
```
--------------------------------
### Development Environment Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configures cloud storage for the development environment, using a local volume adapter and specifying a local upload directory and base URL.
```elixir
# config/dev.exs
import Config
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "priv/uploads/dev",
base_url: "http://localhost:4000/uploads"
```
--------------------------------
### Production Environment Storage Configuration (S3)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure S3 storage for the production environment, dynamically fetching bucket name and credentials from environment variables.
```elixir
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.S3,
bucket: System.get_env("S3_BUCKET"),
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
region: System.get_env("AWS_REGION", "us-east-1")
```
--------------------------------
### Configure Shadcn Theme
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Apply the clean, minimal shadcn/ui inspired theme by setting the `:theme` configuration for `:ash_form_builder` to `AshFormBuilder.Themes.Shadcn`.
```elixir
config :ash_form_builder, :theme, AshFormBuilder.Themes.Shadcn
```
--------------------------------
### Basic File Upload Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Sets up a single file upload field for a resource. Define the field type as `:file_upload` and configure options like `accept`, `max_files`, `max_file_size`, and the `cloud` module.
```elixir
defmodule MyApp.Users.User do
use Ash.Resource,
domain: MyApp.Users,
extensions: [AshFormBuilder]
attributes do
uuid_primary_key :id
attribute :name, :string, allow_nil?: false
attribute :avatar_path, :string
end
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
# Store the uploaded file path in the avatar_path attribute
change fn changeset, _ ->
case Ash.Changeset.get_argument(changeset, :avatar) do
nil -> changeset
path -> Ash.Changeset.change_attribute(changeset, :avatar_path, path)
end
end
end
end
form do
action :create
submit_label "Create User"
field :name do
label "Full Name"
required true
end
field :avatar do
type :file_upload
label "Profile Photo"
hint "JPEG or PNG, max 5 MB"
accept :images # or ~w(.jpg .jpeg .png) — see table below
max_files 1
max_file_size {5, :mb} # also accepts a raw byte integer
cloud MyApp.Buckets.Cloud
end
end
end
```
--------------------------------
### Global Theme Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/guides/theme_customization_guide.md
Configure the default theme and its options globally in `config/config.exs`. This sets up the primary theme and common styling for form elements.
```elixir
# config/config.exs
config :ash_form_builder,
theme: MyAppWeb.FormBuilder.TailwindTheme,
theme_opts: [
wrapper_class: "space-y-6",
field_wrapper_class: "mb-4",
label_class: "block text-sm font-medium mb-1",
input_class: "w-full px-3 py-2 border rounded-md"
]
```
--------------------------------
### Configure Glassmorphism Theme
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Enable the premium glass-effect UI theme by configuring the `:ash_form_builder` application with `AshFormBuilder.Themes.Glassmorphism`.
```elixir
config :ash_form_builder, :theme, AshFormBuilder.Themes.Glassmorphism
```
--------------------------------
### Organize by Date
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configures the upload options to store files in a bucket organized by the current date in ISO 8601 format. This helps in managing and organizing files chronologically.
```elixir
opts upload: [
cloud: MyApp.Cloud,
bucket_name: "documents/#{Date.to_iso8601(:calendar.date())}"
]
```
--------------------------------
### Test Environment Storage Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure local volume storage for the test environment, using a temporary directory that is automatically cleaned.
```elixir
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "tmp/test_uploads", # Auto-cleaned
base_url: "http://localhost:4000/uploads"
```
--------------------------------
### Single Bucket with Prefixes Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Organize files within a single bucket using prefixes for different types. Specify the subdirectory using `bucket_name` in upload options.
```elixir
field :avatar do
type :file_upload
opts upload: [
cloud: MyApp.Buckets.Cloud,
bucket_name: "prod/avatars" # ← Organize by type
]
end
field :document do
type :file_upload
opts upload: [
cloud: MyApp.Buckets.Cloud,
bucket_name: "prod/documents"
]
end
```
--------------------------------
### Automatic File Path Storage (After)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Shows the simplified 'after' state where file path storage is automatic, requiring no manual helper functions.
```elixir
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
# That's it! Automatic storage.
end
end
form do
field :avatar do
type :file_upload
# Auto-stores to :avatar_path
end
end
```
--------------------------------
### Testing File Uploads in LiveView
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
Demonstrates how to test file uploads and deletions within a Phoenix LiveView context. This includes simulating file uploads, submitting forms, and verifying that files are correctly uploaded or marked for deletion.
```elixir
defmodule MyAppWeb.ProjectLive.FormTest do
use MyAppWeb.ConnCase, async: true
import Phoenix.LiveViewTest
test "upload and delete file", %{conn: conn} do
{:ok, view, _html} = live_isolated(conn, MyAppWeb.ProjectLive.Form)
# Upload file
upload =
file_input(view, "#project-form", :proposal, [
%{
name: "proposal.pdf",
content: :binary.copy(<<0x25, 0x50, 0x44, 0x46>>, 100),
type: "application/pdf"
}
])
render_upload(upload, 100)
# Submit form
view
|> form("#project-form", %{"name" => "Test Project"})
|> render_submit()
assert render(view) =~ "Project created successfully!"
end
test "delete existing file in update form", %{conn: conn} do
project = create_project_with_proposal()
{:ok, view, _html} = live_isolated(conn, MyAppWeb.ProjectLive.Form,
params: %{"id" => project.id}
)
# Verify existing file shown
html = render(view)
assert html =~ "proposal.pdf"
# Click delete
view |> element("[phx-value-field=\"proposal\"]") |> render_click()
# Verify delete state
html = render(view)
assert html =~ "Marked for deletion"
# Submit to confirm deletion
view
|> form("#project-form", %{"name" => "Updated Project"})
|> render_submit()
# Verify file was deleted
project = MyApp.Projects.get_project!(project.id)
assert is_nil(project.proposal_path)
end
end
```
--------------------------------
### Configure Cloud Storage Module
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Always specify the cloud storage module and bucket name in production to avoid relying on temporary files.
```elixir
# Don't rely on temp files in production
opts upload: [
cloud: MyApp.Buckets.Cloud, # ← Always specify
bucket_name: "prod/avatars"
]
```
--------------------------------
### Environment-Specific Storage - Test
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Configures file upload storage for the test environment using the Volume adapter, with automatic cleanup.
```elixir
# config/test.exs
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "tmp/test_uploads" # Auto-cleaned
```
--------------------------------
### Configure MishkaTheme
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Integrate MishkaChelekom components for styling by configuring the `:theme` for `:ash_form_builder` to `AshFormBuilder.Theme.MishkaTheme`. Ensure `mishka_chelekom` is a dependency.
```elixir
config :ash_form_builder, :theme, AshFormBuilder.Theme.MishkaTheme
```
--------------------------------
### Set Maximum File Size Limit
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Configure a reasonable maximum file size, such as 10 MB, for most use cases to manage storage efficiently.
```elixir
max_file_size: 10_000_000 # 10 MB for most use cases
```
--------------------------------
### LiveView Integration for Form Creation
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Integrate Ash Form Builder forms into a LiveView for creating new resources. The `mount` function initializes the form, and `render` uses the `AshFormBuilder.FormComponent` to display it.
```elixir
defmodule MyAppWeb.UserLive.Create do
use MyAppWeb, :live_view
def mount(_params, _session, socket) do
form = MyApp.Users.User.Form.for_create(actor: socket.assigns.current_user)
{:ok, assign(socket, form: form)}
end
def render(assigns) do
~H"""
<.live_component
module={AshFormBuilder.FormComponent}
id="user-form"
resource={MyApp.Users.User}
form={@form}
/>
"""
end
def handle_info({:form_submitted, MyApp.Users.User, user}, socket) do
{:noreply, push_navigate(socket, to: ~p"/users/#{user.id}")}
end
end
```
--------------------------------
### Environment-Specific Storage - Development
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Configures file upload storage for the development environment using the Volume adapter.
```elixir
# config/dev.exs
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "priv/uploads/dev"
```
--------------------------------
### Development Environment Storage Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure local volume storage for the development environment, specifying a dedicated directory and base URL.
```elixir
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.Volume,
bucket: "priv/uploads/dev",
base_url: "http://localhost:4000/uploads"
```
--------------------------------
### Fixed Test Resources Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Corrects the configuration for BlogPost many-to-many relationships and ensures proper module ordering for Ash requirements.
```elixir
- Fixed BlogPost many_to_many relationship configuration
- Moved BlogPostCategory module before BlogPost (Ash requirement)
- Added proper actions to join resources
```
--------------------------------
### Scaffold LiveView CRUD Interface
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Command to scaffold a complete Phoenix LiveView CRUD interface for an Ash resource using Ash Form Builder.
```bash
mix ash_form.gen.live -r MyApp.Todos.Task --accent teal --transitions smooth
```
--------------------------------
### Non-Interactive Publish with --replace
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
Use the mix hex.publish --replace command for a non-interactive publish, suitable when you have an API key configured.
```bash
mix hex.publish --replace
```
--------------------------------
### Non-Interactive Publish with HEX_API_KEY
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/PUBLISHING.md
Publish non-interactively by setting the HEX_API_KEY environment variable and using the mix hex.publish --yes command.
```bash
export HEX_API_KEY=your_api_key_here
mix hex.publish --yes
```
--------------------------------
### Automatic File Path Storage (Before)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Demonstrates the manual helper function previously required for storing file paths in AshFormBuilder.
```elixir
actions do
create :create do
accept [:name]
argument :avatar, :string, allow_nil?: true
# Manual helper function required
change fn changeset, _ ->
case Ash.Changeset.get_argument(changeset, :avatar) do
nil -> changeset
path -> Ash.Changeset.change_attribute(changeset, :avatar_path, path)
end
end
end
end
```
--------------------------------
### Multiple Buckets for Different File Types
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
Define separate upload configurations for different file types, each pointing to a dedicated cloud and bucket name. This allows for better organization and management of uploaded files.
```elixir
# Different buckets for different file types
field :avatar do
type :file_upload
opts upload: [
cloud: MyApp.AvatarCloud, # Dedicated avatar bucket
bucket_name: :user_avatars
]
end
field :document do
type :file_upload
opts upload: [
cloud: MyApp.DocumentCloud, # Dedicated document bucket
bucket_name: :user_documents
]
end
```
--------------------------------
### Multi-Tenant Storage - Path-Based Isolation
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Implement multi-tenant storage by including the tenant ID in the bucket name prefix. This organizes files by tenant within a single bucket.
```elixir
field :document do
type :file_upload
opts upload: [
cloud: MyApp.Buckets.Cloud,
bucket_name: "tenants/#{tenant_id}/documents"
]
end
```
--------------------------------
### Environment-Specific Storage - Production
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Configures file upload storage for the production environment using the S3 adapter, retrieving credentials from environment variables.
```elixir
# config/prod.exs
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.S3,
bucket: System.get_env("S3_BUCKET"),
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
region: System.get_env("AWS_REGION")
```
--------------------------------
### Theme System Custom Field Type Extension
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Illustrates how to extend the theme system to support custom field types by providing a custom component injection.
```elixir
defmodule MyApp.MyTheme do
use AshFormBuilder.Theme
# ... other theme callbacks
def render_field(assigns, :my_custom_field_type), do:
~H""
end
```
--------------------------------
### File Metadata Capture - Future Enhancement
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Demonstrates how file metadata can be stored in a separate map attribute in the future.
```elixir
attribute :avatar_metadata, :map do
default %{}
end
```
--------------------------------
### S3 Adapter Configuration (AWS S3 / Compatible)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure the S3 adapter for production or scalable applications. Requires AWS credentials and region. Supports S3-compatible services by optionally specifying an endpoint and path-style URLs.
```elixir
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.S3,
bucket: "my-app-bucket",
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
region: "us-east-1",
# Optional: Custom endpoint for S3-compatible services
# endpoint: "https://nyc3.digitaloceanspaces.com",
# Optional: Force path-style URLs (for MinIO, etc.)
# force_path_style: true
```
--------------------------------
### Field DSL Order Option
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Demonstrates the `:order` option in the field DSL to explicitly define the rendering order of form fields.
```elixir
form MyResource do
field :name, order: 1
field :email, order: 2
end
```
--------------------------------
### Separate Buckets per Type Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure separate cloud modules for each file type, each pointing to a distinct bucket. This is useful for granular control over storage for different asset types.
```elixir
defmodule MyApp.AvatarCloud do
use Buckets.Cloud, otp_app: :my_app
end
defmodule MyApp.DocumentCloud do
use Buckets.Cloud, otp_app: :my_app
end
# Config
config :my_app, MyApp.AvatarCloud,
adapter: Buckets.Adapters.S3,
bucket: "my-app-avatars"
config :my_app, MyApp.DocumentCloud,
adapter: Buckets.Adapters.S3,
bucket: "my-app-documents"
# Usage
field :avatar do
type :file_upload
opts upload: [
cloud: MyApp.AvatarCloud # ← Different cloud module
]
end
```
--------------------------------
### Theme Module Documentation
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Describes the content included in the `@moduledoc` for theme modules, covering configuration, visual characteristics, and browser support.
```elixir
- Configuration examples
- Visual characteristics
- Browser support information
- Usage examples
- Dark mode configuration
```
--------------------------------
### Multiple File Upload Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/FILE_UPLOAD_GUIDE.md
Sets up a file upload field to accept multiple files, specifying the maximum number of entries, file size limits, and accepted file types.
```elixir
field :attachments do
type :file_upload
label "Attachments"
hint "Upload multiple documents (max 5)"
opts upload: [
cloud: MyApp.Cloud,
max_entries: 5,
max_file_size: 10_000_000,
accept: ~w(.pdf .doc .docx)
]
end
```
--------------------------------
### Create Task and Redirect
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
Tests the functionality of creating a task using the generated form and verifies redirection. This snippet is part of the testing suite.
```elixir
defmodule MyAppWeb.TaskLiveTest do
use MyAppWeb.ConnCase
import Phoenix.LiveViewTest
test "creates task and redirects", %{conn: conn} do
{:ok, view, _html} = live_isolated(conn, MyAppWeb.TaskLive.Form)
assert form(view, "#task-form", task: %{
title: "Test Task",
description: "Test description"
}) |> render_submit()
assert_redirect(view, ~p"/tasks/*")
end
end
```
--------------------------------
### Generated LiveView Module (`index.ex`)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/README.md
This LiveView module handles user interactions, parameter synchronization with Cinder.UrlSync, and callbacks for form submission and deletion. It uses Ash for data operations and Cinder for table management.
```elixir
defmodule MyAppWeb.UserLive.Index do
use MyAppWeb, :live_view
use Cinder.UrlSync # injects handle_info for URL sync automatically
alias MyApp.Accounts.User
@collection_id "user-collection"
def mount(_params, _session, socket) do
{:ok, assign(socket, url_state: false, record: nil, form: nil)}
end
def handle_params(params, uri, socket) do
socket = Cinder.UrlSync.handle_params(params, uri, socket)
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
# Triggered by AshFormBuilder.FormComponent after a successful Ash action
def handle_info({:form_submitted, User, _result}, socket) do
{:noreply,
socket
|> put_flash(:info, "User saved successfully.")
|> Cinder.refresh_table(@collection_id) # async re-query, no page reload
|> push_patch(to: ~p"/users")}
end
def handle_event("delete", %{"id" => id}, socket) do
User |> Ash.get!(id, actor: socket.assigns[:current_user])
|> Ash.destroy!(actor: socket.assigns[:current_user])
{:noreply, socket |> put_flash(:info, "User deleted.") |> Cinder.refresh_table(@collection_id)}
end
end
```
--------------------------------
### Migration Step 1: Remove Manual Storage Logic
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
This code snippet shows the manual storage logic that should be removed when migrating to the `:file_upload` type.
```elixir
# Remove this:
change fn changeset, _ ->
# Manual storage logic
end
```
--------------------------------
### Storage Location Configuration - Path-Based
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Configures file upload fields to use path-based organization within a bucket, recommended for clarity.
```elixir
field :avatar do
type :file_upload
opts upload: [
cloud: MyApp.Cloud,
bucket_name: "users/avatars" # Organized by type
]
end
field :document do
type :file_upload
opts upload: [
cloud: MyApp.Cloud,
bucket_name: "documents/contracts"
]
end
```
--------------------------------
### Infer Module - Manage Relationship Functions
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Introduces new functions for detecting and inferring `manage_relationship` configurations within the Infer Module.
```elixir
- New functions: `process_manage_relationships/3`, `infer_manage_relationship/3`
```
--------------------------------
### GCS Adapter Configuration (Google Cloud Storage)
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Configure the GCS adapter for applications on Google Cloud Platform. Requires bucket name, service account credentials, and project ID.
```elixir
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.GCS,
bucket: "my-app-bucket",
service_account_credentials: System.get_env("GCP_SERVICE_ACCOUNT_JSON"),
project_id: "my-gcp-project"
```
--------------------------------
### Dev/Test Dependencies
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/CHANGELOG.md
Adds Dialyxir and Credo for development and testing purposes only. These are not required at runtime.
```elixir
"""
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
"""
```
--------------------------------
### Compress Images Before Upload
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Compresses images using Image.resize and Image.write functions. Ensure the Image module is available and the temporary path is correctly specified.
```elixir
# Compress images before upload
{:ok, compressed} =
Image.open(path)
|> Image.resize({800, 800})
|> Image.write(temp_path)
```
--------------------------------
### Organize Files in Cloud Storage
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/ENHANCEMENTS_SUMMARY.md
Organize files by type or user to maintain a structured storage system, rather than using a generic 'uploads' bucket.
```elixir
bucket_name: "users/avatars" # Not just "uploads"
```
--------------------------------
### Base Cloud Configuration
Source: https://github.com/nagieeb0/ash_form_builder/blob/main/STORAGE_CONFIGURATION.md
Defines the base configuration for the application's cloud storage, specifying the adapter (e.g., S3) and necessary credentials like access key ID, secret access key, and region.
```elixir
# config/config.exs
import Config
# Base cloud configuration
config :my_app, MyApp.Buckets.Cloud,
adapter: Buckets.Adapters.S3,
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
region: System.get_env("AWS_REGION", "us-east-1")
# Environment-specific overrides
import_config "#{config_env()}.exs"
```