### 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" ```