Packages
phoenix_kit
1.7.13
1.7.210
1.7.209
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
Current section
Files
lib/phoenix_kit_web/live/modules/ai/prompts.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
page_title={@page_title}
current_path={@current_path}
project_title={@project_title}
phoenix_kit_current_scope={@phoenix_kit_current_scope}
current_locale={assigns[:current_locale]}
>
<div class="p-6">
<%!-- Header --%>
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-2xl font-bold">AI Prompts</h1>
<p class="text-base-content/70 mt-1">
Reusable prompt templates with variable substitution
</p>
</div>
<.link
navigate={PhoenixKit.Utils.Routes.ai_path() <> "/prompts/new"}
class="btn btn-primary"
>
<.icon name="hero-plus" class="w-5 h-5 mr-2" /> New Prompt
</.link>
</div>
<%!-- Tabs (shared with Endpoints) --%>
<div class="tabs tabs-boxed mb-6">
<.link
navigate={PhoenixKit.Utils.Routes.ai_path() <> "/endpoints"}
class="tab"
>
<.icon name="hero-server-stack" class="w-4 h-4 mr-2" /> Endpoints
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.ai_path() <> "/usage"}
class="tab"
>
<.icon name="hero-chart-bar" class="w-4 h-4 mr-2" /> Usage
</.link>
<.link
navigate={PhoenixKit.Utils.Routes.ai_path() <> "/prompts"}
class="tab tab-active"
>
<.icon name="hero-document-text" class="w-4 h-4 mr-2" /> Prompts
</.link>
</div>
<%!-- Content --%>
<%= if Enum.empty?(@prompts) do %>
<%!-- Empty State --%>
<div class="card bg-base-200 p-8 text-center">
<div class="flex flex-col items-center gap-4">
<.icon name="hero-document-text" class="w-16 h-16 text-base-content/30" />
<h2 class="text-xl font-semibold">No Prompts Yet</h2>
<p class="text-base-content/70 max-w-md">
Create reusable prompt templates with variable substitution.
Use <code class="bg-base-300 px-1 rounded">{"{{VariableName}}"}</code>
syntax for dynamic content.
</p>
<.link
navigate={PhoenixKit.Utils.Routes.ai_path() <> "/prompts/new"}
class="btn btn-primary mt-4"
>
<.icon name="hero-plus" class="w-5 h-5 mr-2" /> Create First Prompt
</.link>
</div>
</div>
<% else %>
<%!-- Sort Controls --%>
<div class="flex items-center gap-2 mb-4 flex-wrap">
<span class="text-sm text-base-content/70">Sort by:</span>
<%= for {field, label} <- @sort_options do %>
<button
phx-click="sort"
phx-value-by={field}
class={"btn btn-xs #{if @sort_by == field, do: "btn-primary", else: "btn-ghost"}"}
>
{label}
<%= if @sort_by == field do %>
<.icon
name={if @sort_dir == :asc, do: "hero-chevron-up", else: "hero-chevron-down"}
class="w-3 h-3 ml-1"
/>
<% end %>
</button>
<% end %>
</div>
<%!-- Prompts Grid --%>
<div class="grid gap-4">
<%= for prompt <- @prompts do %>
<div class={"card bg-base-200 #{if !prompt.enabled, do: "opacity-60"}"}>
<div class="card-body p-4">
<div class="flex items-start justify-between">
<div class="flex-1">
<div class="flex items-center gap-3 flex-wrap">
<span class="badge badge-ghost font-mono">#{prompt.id}</span>
<.link
navigate={PhoenixKit.Utils.Routes.ai_path() <> "/prompts/#{prompt.id}/edit"}
class="font-semibold text-lg hover:text-primary hover:underline"
>
{prompt.name}
</.link>
<span class={"badge #{if prompt.enabled, do: "badge-success", else: "badge-neutral"}"}>
{if prompt.enabled, do: "Active", else: "Disabled"}
</span>
<span class="badge badge-outline font-mono text-xs">{prompt.slug}</span>
</div>
<%!-- Variables --%>
<%= if prompt.variables && length(prompt.variables) > 0 do %>
<div class="mt-2 flex flex-wrap gap-2">
<span class="text-sm text-base-content/70">Variables:</span>
<%= for var <- prompt.variables do %>
<span class="badge badge-sm badge-primary badge-outline font-mono">
{"{{#{var}}}"}
</span>
<% end %>
</div>
<% end %>
<%!-- Content Preview --%>
<div class="mt-2 text-sm text-base-content/70 line-clamp-2">
{PhoenixKit.AI.Prompt.content_preview(prompt.content)}
</div>
<%!-- Usage Stats --%>
<div class="mt-2 flex flex-wrap gap-4 text-sm">
<div class="flex items-center gap-1 text-base-content/70" title="Usage count">
<.icon name="hero-arrow-path" class="w-4 h-4" />
<span>{prompt.usage_count} uses</span>
</div>
<div class="flex items-center gap-1 text-base-content/70" title="Last used">
<.icon name="hero-clock" class="w-4 h-4" />
<span>
<%= if prompt.last_used_at do %>
<.time_ago datetime={prompt.last_used_at} />
<% else %>
Never used
<% end %>
</span>
</div>
<div class="flex items-center gap-1 text-base-content/70" title="Created">
<.icon name="hero-calendar" class="w-4 h-4" />
<span>
<.time_ago datetime={prompt.inserted_at} />
</span>
</div>
</div>
<%= if prompt.description do %>
<p class="mt-2 text-sm text-base-content/60">{prompt.description}</p>
<% end %>
</div>
<%!-- Actions --%>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm btn-square">
<.icon name="hero-ellipsis-vertical" class="w-5 h-5" />
</div>
<ul
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow-lg bg-base-100 rounded-box w-48"
>
<li>
<.link navigate={PhoenixKit.Utils.Routes.ai_path() <> "/prompts/#{prompt.id}/edit"}>
<.icon name="hero-pencil" class="w-4 h-4" /> Edit
</.link>
</li>
<li>
<button phx-click="toggle_prompt" phx-value-id={prompt.id}>
<.icon
name={if prompt.enabled, do: "hero-pause", else: "hero-play"}
class="w-4 h-4"
/>
{if prompt.enabled, do: "Disable", else: "Enable"}
</button>
</li>
<li class="divider"></li>
<li>
<button
phx-click="delete_prompt"
phx-value-id={prompt.id}
data-confirm="Are you sure you want to delete this prompt?"
class="text-error"
>
<.icon name="hero-trash" class="w-4 h-4" /> Delete
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>