Packages

phoenix_kit

1.7.26
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 entities.html.heex
Raw

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

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={
gettext("%{project_title} %{section}",
project_title: @project_title,
section: gettext("Entities")
)
}
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 --%>
<header class="w-full relative mb-6">
<%!-- Back Button (Left aligned) --%>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/modules")}
class="btn btn-outline btn-primary btn-sm absolute left-0 top-0 -mb-12"
>
<.icon name="hero-arrow-left" class="w-4 h-4 mr-2" /> {gettext("Back to Modules")}
</.link>
<%!-- Title Section --%>
<div class="text-center">
<h1 class="text-4xl font-bold text-base-content mb-3">{gettext("Entity Manager")}</h1>
<p class="text-lg text-base-content">
{gettext("Create and manage custom content types with dynamic fields")}
</p>
</div>
</header>
<%!-- Stats Cards --%>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<.stat_card
color="primary"
value={@total_entities}
title={gettext("Total Entities")}
subtitle={gettext("Content types defined")}
>
<:icon>
<.icon name="hero-rectangle-stack" class="w-6 h-6" />
</:icon>
</.stat_card>
<.stat_card
color="success"
value={@active_entities}
title={gettext("Published")}
subtitle={gettext("Ready for data creation")}
>
<:icon>
<.icon name="hero-bolt" class="w-6 h-6" />
</:icon>
</.stat_card>
<.stat_card
color="secondary"
value={@total_data_records}
title={gettext("Data Records")}
subtitle={gettext("Across all entities")}
>
<:icon>
<.icon name="hero-circle-stack" class="w-6 h-6" />
</:icon>
</.stat_card>
</div>
<%!-- Action Bar --%>
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
<div>
<h2 class="text-2xl font-semibold text-base-content">{gettext("All Entities")}</h2>
<p class="text-base-content/70">
{gettext("Manage custom content types and field definitions")}
</p>
</div>
<div class="flex gap-2 items-center">
<%!-- View Mode Toggle --%>
<div class="join">
<button
type="button"
phx-click="toggle_view_mode"
phx-value-mode="card"
class={["btn join-item", @view_mode == "card" && "btn-active"]}
title={gettext("Card view")}
>
<.icon name="hero-squares-2x2" class="w-4 h-4" />
</button>
<button
type="button"
phx-click="toggle_view_mode"
phx-value-mode="table"
class={["btn join-item", @view_mode == "table" && "btn-active"]}
title={gettext("Table view")}
>
<.icon name="hero-bars-3-bottom-left" class="w-4 h-4" />
</button>
</div>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/new")}
class="btn btn-primary"
>
<.icon name="hero-plus" class="w-4 h-4 mr-2" /> {gettext("New Entity")}
</.link>
</div>
</div>
<%!-- Filters Section --%>
<div class="card bg-base-100 shadow-xl mb-6">
<div class="card-body">
<h2 class="card-title text-xl mb-4">
<.icon name="hero-funnel" class="w-5 h-5" /> {gettext("Filters & Search")}
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<%!-- Status Filter --%>
<div>
<label class="label">
<span class="label-text">{gettext("Filter by Status")}</span>
</label>
<.form for={%{}} phx-change="filter_by_status">
<select class="select select-bordered w-full" name="status">
<option value="all" selected={@selected_status == "all"}>
{gettext("All Statuses")}
</option>
<option value="published" selected={@selected_status == "published"}>
{gettext("Published")}
</option>
<option value="draft" selected={@selected_status == "draft"}>
{gettext("Draft")}
</option>
<option value="archived" selected={@selected_status == "archived"}>
{gettext("Archived")}
</option>
</select>
</.form>
</div>
<%!-- Search --%>
<div>
<label class="label">
<span class="label-text">{gettext("Search Entities")}</span>
</label>
<.form for={%{}} phx-change="search" phx-submit="search" class="join w-full">
<input
type="text"
name="search[term]"
value={@search_term}
placeholder={gettext("Search by name or display name...")}
class="input input-bordered join-item flex-1"
/>
<button type="submit" class="btn btn-primary join-item">
<.icon name="hero-magnifying-glass" class="w-4 h-4" />
</button>
</.form>
</div>
</div>
</div>
</div>
<%!-- Entities Grid --%>
<%= if Enum.empty?(@entities) do %>
<%!-- Empty State --%>
<div class="card bg-base-100 shadow-xl border-2 border-dashed border-base-300">
<div class="card-body text-center py-12">
<div class="text-6xl mb-4 opacity-50"><.icon name="hero-cube" class="w-6 h-6" /></div>
<%= if @total_entities == 0 do %>
<%!-- No entities exist at all --%>
<h3 class="text-2xl font-semibold text-base-content/60 mb-4">
{gettext("No Entities Yet")}
</h3>
<p class="text-base-content/50 mb-6 max-w-md mx-auto">
{gettext(
"Get started by creating your first custom content type. Think brands, products, team members, or any structured content you need."
)}
</p>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/new")}
class="btn btn-primary btn-lg"
>
<.icon name="hero-plus" class="w-5 h-5 mr-2" /> {gettext("Create Your First Entity")}
</.link>
<% else %>
<%= if @selected_status != "all" || @search_term != "" do %>
<%!-- Entities exist but filters exclude them --%>
<h3 class="text-2xl font-semibold text-base-content/60 mb-4">
{gettext("No Entities Found")}
</h3>
<p class="text-base-content/50 mb-6 max-w-md mx-auto">
{gettext(
"No entities match your current filters. Try adjusting your search criteria or clearing the filters."
)}
</p>
<button phx-click="clear_filters" class="btn btn-outline btn-lg">
<.icon name="hero-x-mark" class="w-5 h-5 mr-2" /> {gettext("Clear Filters")}
</button>
<% else %>
<%!-- True empty state --%>
<h3 class="text-2xl font-semibold text-base-content/60 mb-4">
{gettext("No Entities Yet")}
</h3>
<p class="text-base-content/50 mb-6 max-w-md mx-auto">
{gettext(
"Get started by creating your first custom content type. Think brands, products, team members, or any structured content you need."
)}
</p>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/new")}
class="btn btn-primary btn-lg"
>
<.icon name="hero-plus" class="w-5 h-5 mr-2" /> {gettext(
"Create Your First Entity"
)}
</.link>
<% end %>
<% end %>
</div>
</div>
<% else %>
<%!-- Entities List --%>
<%= if @view_mode == "table" do %>
<%!-- Table View --%>
<.table_default variant="zebra" size="sm">
<.table_default_header>
<.table_default_row>
<.table_default_header_cell>{gettext("Entity")}</.table_default_header_cell>
<.table_default_header_cell>{gettext("Status")}</.table_default_header_cell>
<.table_default_header_cell>{gettext("Fields")}</.table_default_header_cell>
<.table_default_header_cell>{gettext("Created")}</.table_default_header_cell>
<.table_default_header_cell>{gettext("Actions")}</.table_default_header_cell>
</.table_default_row>
</.table_default_header>
<.table_default_body>
<%= for entity <- @entities do %>
<.table_default_row>
<.table_default_cell>
<.link
navigate={
PhoenixKit.Utils.Routes.locale_aware_path(
assigns,
"/admin/entities/#{entity.name}/data"
)
}
class="flex items-center gap-3 hover:text-primary transition-colors cursor-pointer group"
>
<div class="text-2xl">
<%= if entity.icon do %>
<.icon name={entity.icon} class="w-6 h-6" />
<% else %>
<.icon name="hero-cube" class="w-6 h-6" />
<% end %>
</div>
<div>
<div class="font-bold">
{entity.display_name}
</div>
<div class="text-sm opacity-50">
<.icon name="hero-link" class="w-3 h-3 inline" />
{entity.name}
</div>
<%= if entity.description do %>
<div class="text-xs opacity-50 line-clamp-1 mt-1">
{entity.description}
</div>
<% end %>
</div>
</.link>
</.table_default_cell>
<.table_default_cell>
<.content_status_badge status={entity.status} />
</.table_default_cell>
<.table_default_cell>
<div class="flex items-center gap-1">
<.icon name="hero-list-bullet" class="w-4 h-4" />
<span>
{length(entity.fields_definition || [])}
</span>
</div>
</.table_default_cell>
<.table_default_cell>
<span class="text-sm">
{PhoenixKit.Utils.Date.format_date_with_user_format(entity.date_created)}
</span>
</.table_default_cell>
<.table_default_cell>
<div class="flex gap-2">
<.link
navigate={
PhoenixKit.Utils.Routes.locale_aware_path(
assigns,
"/admin/entities/#{entity.name}/data"
)
}
class="btn btn-ghost btn-xs"
title={gettext("Go to Data")}
>
<.icon name="hero-arrow-right" class="w-4 h-4" />
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/#{entity.id}/edit")}
class="btn btn-ghost btn-xs"
title={gettext("Edit")}
>
<.icon name="hero-pencil" class="w-4 h-4" />
</.link>
<%= if entity.status == "archived" do %>
<button
class="btn btn-ghost btn-xs text-success"
phx-click="restore_entity"
phx-value-id={entity.id}
title={gettext("Restore")}
>
<.icon name="hero-arrow-path" class="w-4 h-4" />
</button>
<% else %>
<button
class="btn btn-ghost btn-xs text-error"
phx-click="archive_entity"
phx-value-id={entity.id}
title={gettext("Archive")}
>
<.icon name="hero-trash" class="w-4 h-4" />
</button>
<% end %>
</div>
</.table_default_cell>
</.table_default_row>
<% end %>
</.table_default_body>
</.table_default>
<% else %>
<%!-- Card View --%>
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<%= for entity <- @entities do %>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-shadow">
<div class="card-body">
<div class="flex items-start justify-between mb-4">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/#{entity.name}/data")}
class="flex items-center hover:text-primary transition-colors cursor-pointer group"
>
<div class="text-2xl mr-3">
<%= if entity.icon do %>
<.icon name={entity.icon} class="w-6 h-6" />
<% else %>
<.icon name="hero-cube" class="w-6 h-6" />
<% end %>
</div>
<div>
<h3 class="card-title text-lg">
{entity.display_name}
</h3>
<p class="text-sm opacity-50">
<.icon name="hero-link" class="w-3 h-3 inline" />
{entity.name}
</p>
</div>
</.link>
<%!-- Status Badge --%>
<.content_status_badge status={entity.status} />
</div>
<%= if entity.description do %>
<p class="text-sm text-base-content/70 mb-4 line-clamp-2">
{entity.description}
</p>
<% end %>
<%!-- Field Count --%>
<div class="flex items-center justify-between text-sm text-base-content/60 mb-4">
<div class="flex items-center">
<.icon name="hero-list-bullet" class="w-4 h-4 mr-1" />
<span>
{length(entity.fields_definition || [])}
{if length(entity.fields_definition || []) == 1,
do: gettext("field"),
else: gettext("fields")}
</span>
</div>
<%= if entity.creator do %>
<span class="badge badge-outline badge-xs">
{gettext("by")} {entity.creator.email}
</span>
<% end %>
</div>
<%!-- Actions --%>
<div class="card-actions justify-end">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/#{entity.name}/data")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-arrow-right" class="w-4 h-4 mr-1" /> {gettext("Go to Data")}
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/entities/#{entity.id}/edit")}
class="btn btn-primary btn-sm"
>
<.icon name="hero-pencil" class="w-4 h-4 mr-1" /> {gettext("Edit")}
</.link>
<%!-- Archive/Restore Button --%>
<%= if entity.status == "archived" do %>
<button
class="btn btn-success btn-sm"
phx-click="restore_entity"
phx-value-id={entity.id}
title={gettext("Restore entity")}
>
<.icon name="hero-arrow-path" class="w-4 h-4" />
</button>
<% else %>
<button
class="btn btn-error btn-sm"
phx-click="archive_entity"
phx-value-id={entity.id}
title={gettext("Archive entity")}
>
<.icon name="hero-trash" class="w-4 h-4" />
</button>
<% end %>
</div>
<%!-- Created Date --%>
<div class="text-xs text-base-content/50 mt-2 pt-2 border-t border-base-300">
{gettext("Created")} {PhoenixKit.Utils.Date.format_date_with_user_format(
entity.date_created
)}
</div>
</div>
</div>
<% end %>
</div>
<% end %>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>