Packages

phoenix_kit

1.7.81
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules entities web data_form.html.heex
Raw

lib/modules/entities/web/data_form.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={
gettext("%{project_title} %{action} %{entity}",
project_title: @project_title,
action: if(@data_record.uuid, do: gettext("Edit"), else: gettext("New")),
entity: @entity.display_name
)
}
current_path={@url_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="container flex flex-col mx-auto px-4 py-6">
<%!-- Header Section --%>
<.admin_page_header back={
PhoenixKit.Utils.Routes.path("/admin/entities/#{@entity.name}/data")
}>
<h1 class="text-xl sm:text-2xl lg:text-3xl font-bold text-base-content">
<%= if @data_record.uuid do %>
{gettext("Edit %{entity}", entity: @entity.display_name)}
<% else %>
{gettext("Create New %{entity}", entity: @entity.display_name)}
<% end %>
</h1>
<p class="text-sm text-base-content/60 mt-0.5">
<%= if @data_record.uuid do %>
{gettext("Update data for the %{entity} entity", entity: @entity.display_name)}
<% else %>
{gettext("Add data for the %{entity} entity", entity: @entity.display_name)}
<% end %>
</p>
</.admin_page_header>
<%!-- Readonly Banner --%>
<%= if @readonly? do %>
<div class="alert alert-info mb-6">
<.icon name="hero-eye" class="w-5 h-5" />
<span>
{gettext(
"This record is currently being edited by another user. You are in view-only mode."
)}
</span>
</div>
<% end %>
<%!-- Edit Mode Form --%>
<.form
:let={f}
for={@changeset}
phx-change="validate"
phx-debounce="500"
phx-submit="save"
class="space-y-8"
>
<div class="flex justify-end">
<button
type="submit"
class="btn btn-primary"
disabled={@readonly?}
>
<%= if @data_record.uuid do %>
{gettext("Update %{entity}", entity: @entity.display_name)}
<% else %>
{gettext("Create %{entity}", entity: @entity.display_name)}
<% end %>
</button>
</div>
<%= if @show_multilang_tabs do %>
<%!-- Multilang: unified card with language tabs wrapping all content --%>
<% lang_data = get_lang_data(@changeset, @current_lang, @multilang_enabled) %>
<div class="card bg-base-100 shadow-xl">
<.multilang_tabs
multilang_enabled={@multilang_enabled}
language_tabs={@language_tabs}
current_lang={@current_lang}
/>
<.multilang_fields_wrapper
multilang_enabled={@multilang_enabled}
current_lang={@current_lang}
>
<:skeleton>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<div class="skeleton h-4 w-24"></div>
<div class="skeleton h-12 w-full"></div>
</div>
<div class="space-y-2">
<div class="skeleton h-4 w-32"></div>
<div class="skeleton h-12 w-full"></div>
<div class="skeleton h-3 w-48"></div>
</div>
</div>
<div class="divider my-2"></div>
<div class="space-y-4">
<div class="space-y-2">
<div class="skeleton h-4 w-24"></div>
<div class="skeleton h-12 w-full"></div>
</div>
<div class="space-y-2">
<div class="skeleton h-4 w-40"></div>
<div class="skeleton h-24 w-full"></div>
</div>
</div>
</:skeleton>
<div class="divider mx-6 my-0"></div>
<%!-- Tab content: Title & Slug (translatable) --%>
<div class="card-body pt-4 pb-4">
<h3 class="text-sm font-semibold text-base-content/70 uppercase tracking-wide mb-3">
<.icon name="hero-information-circle" class="w-4 h-4 inline -mt-0.5" />
{gettext("Basic Information")}
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<.translatable_field
field_name="title"
form_prefix="phoenix_kit_entity_data"
changeset={@changeset}
schema_field={:title}
multilang_enabled={@multilang_enabled}
current_lang={@current_lang}
primary_language={@primary_language}
lang_data={lang_data}
label={gettext("Title")}
placeholder={gettext("Enter a title for this record")}
required
disabled={@readonly?}
class="w-full"
/>
<.translatable_field
field_name="slug"
form_prefix="phoenix_kit_entity_data"
changeset={@changeset}
schema_field={:slug}
multilang_enabled={@multilang_enabled}
current_lang={@current_lang}
primary_language={@primary_language}
lang_data={lang_data}
label={gettext("Slug (URL-friendly identifier)")}
placeholder={gettext("auto-generated-slug")}
disabled={@readonly?}
class="w-full"
pattern="[a-z0-9]+(?:-[a-z0-9]+)*"
title={gettext("Use lowercase letters, numbers, and hyphens only.")}
hint={gettext("Leave empty to auto-generate from title")}
secondary_hint={gettext("Leave empty to use the primary language slug")}
>
<:label_extra>
<button
type="button"
class="btn btn-ghost btn-xs ml-2"
phx-click="generate_slug"
title={gettext("Generate from title")}
disabled={@readonly?}
>
<.icon name="hero-arrow-path" class="w-3 h-3" /> {gettext("Generate")}
</button>
</:label_extra>
</.translatable_field>
</div>
</div>
<%= if @entity.fields_definition != nil and @entity.fields_definition != [] do %>
<div class="divider mx-6 my-0"></div>
<%!-- Tab content: Custom Fields (translatable) --%>
<div class="card-body pt-4">
<h3 class="text-sm font-semibold text-base-content/70 uppercase tracking-wide mb-3">
<.icon name="hero-list-bullet" class="w-4 h-4 inline -mt-0.5" />
{gettext("Custom Fields")}
</h3>
<%!-- Dynamic form fields generated by FormBuilder --%>
{PhoenixKit.Modules.Entities.FormBuilder.build_fields(@entity, f,
wrapper_class: "mb-6",
disabled: @readonly?,
lang_code: if(@multilang_enabled, do: @current_lang, else: nil)
)}
</div>
<% end %>
</.multilang_fields_wrapper>
</div>
<%!-- Record Settings (non-translatable, separate card) --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-lg mb-4">
<.icon name="hero-cog-6-tooth" class="w-5 h-5" />
{gettext("Record Settings")}
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<%!-- Status --%>
<div>
<.label for="phoenix_kit_entity_data_status">{gettext("Status")}</.label>
<select
name="phoenix_kit_entity_data[status]"
class="select select-bordered w-full"
disabled={@readonly?}
>
<option
value="draft"
selected={Ecto.Changeset.get_field(@changeset, :status) == "draft"}
>
{gettext("Draft")}
</option>
<option
value="published"
selected={Ecto.Changeset.get_field(@changeset, :status) == "published"}
>
{gettext("Published")}
</option>
<option
value="archived"
selected={Ecto.Changeset.get_field(@changeset, :status) == "archived"}
>
{gettext("Archived")}
</option>
</select>
</div>
<%!-- Entity Type (Read-only) --%>
<div>
<.label>{gettext("Entity Type")}</.label>
<div class="input input-bordered w-full bg-base-200 flex items-center">
<%= if @entity.icon do %>
<.icon name={@entity.icon} class="w-4 h-4 mr-2" />
<% end %>
{@entity.display_name}
</div>
<input
type="hidden"
name="phoenix_kit_entity_data[entity_uuid]"
value={@entity.uuid}
/>
</div>
</div>
</div>
</div>
<% else %>
<%!-- Non-multilang: separate cards (original layout) --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl mb-4">
<.icon name="hero-information-circle" class="w-6 h-6" />
{gettext("Basic Information")}
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<%!-- Title --%>
<div>
<.label for="phoenix_kit_entity_data_title">{gettext("Title")} *</.label>
<input
type="text"
name="phoenix_kit_entity_data[title]"
id="phoenix_kit_entity_data_title"
value={Ecto.Changeset.get_field(@changeset, :title) || ""}
placeholder={gettext("Enter a title for this record")}
class="input input-bordered w-full"
phx-debounce="300"
required
disabled={@readonly?}
/>
</div>
<%!-- Slug with Generator --%>
<div>
<.label for="phoenix_kit_entity_data_slug">
{gettext("Slug (URL-friendly identifier)")}
<button
type="button"
class="btn btn-ghost btn-xs ml-2"
phx-click="generate_slug"
title={gettext("Generate from title")}
disabled={@readonly?}
>
<.icon name="hero-arrow-path" class="w-3 h-3" /> {gettext("Generate")}
</button>
</.label>
<input
type="text"
name="phoenix_kit_entity_data[slug]"
id="phoenix_kit_entity_data_slug"
value={Ecto.Changeset.get_field(@changeset, :slug) || ""}
placeholder={gettext("auto-generated-slug")}
class="input input-bordered w-full"
pattern="[a-z0-9]+(?:-[a-z0-9]+)*"
title={gettext("Use lowercase letters, numbers, and hyphens only.")}
phx-debounce="300"
disabled={@readonly?}
/>
<.label class="label">
<span class="label-text-alt">
{gettext("Leave empty to auto-generate from title")}
</span>
</.label>
</div>
<%!-- Status --%>
<div>
<.label for="phoenix_kit_entity_data_status">{gettext("Status")}</.label>
<select
name="phoenix_kit_entity_data[status]"
class="select select-bordered w-full"
disabled={@readonly?}
>
<option
value="draft"
selected={Ecto.Changeset.get_field(@changeset, :status) == "draft"}
>
{gettext("Draft")}
</option>
<option
value="published"
selected={Ecto.Changeset.get_field(@changeset, :status) == "published"}
>
{gettext("Published")}
</option>
<option
value="archived"
selected={Ecto.Changeset.get_field(@changeset, :status) == "archived"}
>
{gettext("Archived")}
</option>
</select>
</div>
<%!-- Entity Type (Read-only) --%>
<div>
<.label>{gettext("Entity Type")}</.label>
<div class="input input-bordered w-full bg-base-200 flex items-center">
<%= if @entity.icon do %>
<.icon name={@entity.icon} class="w-4 h-4 mr-2" />
<% end %>
{@entity.display_name}
</div>
<input
type="hidden"
name="phoenix_kit_entity_data[entity_uuid]"
value={@entity.uuid}
/>
</div>
</div>
</div>
</div>
<%= if @entity.fields_definition != nil and @entity.fields_definition != [] do %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl mb-4">
<.icon name="hero-list-bullet" class="w-6 h-6" /> {gettext("Custom Fields")}
</h2>
<%!-- Dynamic form fields generated by FormBuilder --%>
{PhoenixKit.Modules.Entities.FormBuilder.build_fields(@entity, f,
wrapper_class: "mb-6",
disabled: @readonly?,
lang_code: nil
)}
</div>
</div>
<% end %>
<% end %>
<%!-- Form Actions --%>
<div class="flex justify-between items-center">
<div class="flex gap-2">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/#{@entity.name}/data")}
class="btn btn-outline"
>
{gettext("Cancel")}
</.link>
<button type="button" phx-click="reset" class="btn btn-warning" disabled={@readonly?}>
<.icon name="hero-arrow-path" class="w-4 h-4" />
{gettext("Reset Changes")}
</button>
</div>
<button
type="submit"
class="btn btn-primary"
disabled={@readonly?}
>
<.icon name="hero-check" class="w-4 h-4 mr-2" />
<%= if @data_record.uuid do %>
{gettext("Update %{entity}", entity: @entity.display_name)}
<% else %>
{gettext("Create %{entity}", entity: @entity.display_name)}
<% end %>
</button>
</div>
</.form>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>