Packages

phoenix_kit

1.7.21
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 tickets web user_details.html.heex
Raw

lib/modules/tickets/web/user_details.html.heex

<PhoenixKitWeb.Layouts.dashboard {assigns}>
<div class="container mx-auto">
<%!-- Back Button --%>
<div class="mb-6">
<.link
navigate={PhoenixKit.Utils.Routes.path("/dashboard/tickets")}
class="btn btn-ghost btn-sm gap-2"
>
<.icon name="hero-arrow-left" class="h-4 w-4" />
{gettext("Back to Tickets")}
</.link>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<%!-- Main Content --%>
<div class="lg:col-span-2 space-y-6">
<%!-- Ticket Info Card --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<div class="flex items-start justify-between">
<div class="flex-1 min-w-0">
<h1 class="card-title text-2xl">{@ticket.title}</h1>
<div class="flex items-center gap-3 mt-2 text-sm text-base-content/70">
<span>#{String.slice(@ticket.id, 0, 8)}</span>
<span>•</span>
<span>
{gettext("Created %{date}",
date: Calendar.strftime(@ticket.inserted_at, "%B %d, %Y")
)}
</span>
</div>
</div>
<div class="flex-shrink-0 ml-4">
<%= case @ticket.status do %>
<% "open" -> %>
<span class="badge badge-info badge-lg">{gettext("Open")}</span>
<% "in_progress" -> %>
<span class="badge badge-warning badge-lg">{gettext("In Progress")}</span>
<% "resolved" -> %>
<span class="badge badge-success badge-lg">{gettext("Resolved")}</span>
<% "closed" -> %>
<span class="badge badge-lg">{gettext("Closed")}</span>
<% end %>
</div>
</div>
<div class="divider"></div>
<%!-- Description --%>
<div class="prose max-w-none">
<p class="whitespace-pre-wrap">{@ticket.description}</p>
</div>
<%!-- Attachments Section --%>
<%= if @attachments_enabled and Enum.any?(@attachments) do %>
<div class="divider"></div>
<h3 class="font-semibold mb-3">
{gettext("Attachments")} ({length(@attachments)})
</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<%= for attachment <- @attachments do %>
<%= if attachment.file do %>
<div class="border border-base-300 rounded-lg overflow-hidden">
<%= if attachment.file.file_type == "image" do %>
<a
href={
PhoenixKit.Modules.Storage.get_public_url_by_variant(
attachment.file,
"original"
)
}
target="_blank"
class="block"
>
<img
src={
PhoenixKit.Modules.Storage.get_public_url_by_variant(
attachment.file,
"thumbnail"
)
}
alt={attachment.caption || attachment.file.original_file_name}
class="w-full h-24 object-cover"
/>
</a>
<% else %>
<a
href={
PhoenixKit.Modules.Storage.get_public_url_by_variant(
attachment.file,
"original"
)
}
target="_blank"
class="block"
>
<div class="w-full h-24 flex items-center justify-center bg-base-200">
<.icon name="hero-document" class="w-8 h-8 text-base-content/50" />
</div>
</a>
<% end %>
<div class="p-2">
<div class="text-xs truncate">{attachment.file.original_file_name}</div>
</div>
</div>
<% end %>
<% end %>
</div>
<% end %>
</div>
</div>
<%!-- Comments Section --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<h2 class="card-title">{gettext("Comments")} ({length(@comments)})</h2>
<%!-- Comments List --%>
<div class="space-y-4 mt-4">
<%= if Enum.empty?(@comments) do %>
<div class="text-center py-8 text-base-content/50">
{gettext("No comments yet.")}
</div>
<% else %>
<%= for comment <- @comments do %>
<div class="bg-base-200 rounded-lg p-4">
<div class="flex items-center gap-2 mb-2">
<.user_avatar user={comment.user} size="sm" />
<div>
<span class="font-medium">{comment.user.email}</span>
<span class="text-sm text-base-content/50 ml-2">
{Calendar.strftime(comment.inserted_at, "%b %d, %Y %H:%M")}
</span>
</div>
</div>
<div class="whitespace-pre-wrap pl-10">{comment.content}</div>
</div>
<% end %>
<% end %>
</div>
<div class="divider"></div>
<%!-- Add Comment Form --%>
<form phx-submit="add_comment" phx-change="validate" class="space-y-4">
<div class="form-control">
<label class="label">
<span class="label-text font-medium">{gettext("Add a comment")}</span>
</label>
<textarea
name="comment[content]"
class="textarea textarea-bordered w-full"
rows="3"
placeholder={gettext("Write your comment here...")}
required
></textarea>
</div>
<%!-- Comment Attachments Upload --%>
<%= if @attachments_enabled and Map.has_key?(assigns, :uploads) do %>
<%!-- Already Uploaded Files --%>
<%= if Enum.any?(@pending_comment_files) do %>
<div class="flex flex-wrap gap-2">
<%= for file <- @pending_comment_files do %>
<div class="flex items-center gap-2 bg-base-200 rounded-lg px-3 py-1">
<.icon name="hero-document" class="w-4 h-4 text-base-content/50" />
<span class="text-sm truncate max-w-32">{file.original_file_name}</span>
<button
type="button"
phx-click="remove_pending_comment_file"
phx-value-id={file.id}
class="btn btn-ghost btn-xs"
>
<.icon name="hero-x-mark" class="w-3 h-3" />
</button>
</div>
<% end %>
</div>
<% end %>
<%!-- Upload Progress --%>
<%= for entry <- @uploads.comment_attachments.entries do %>
<div class="flex items-center gap-3 p-2 bg-base-200 rounded-lg">
<.icon name="hero-document" class="w-5 h-5 text-base-content/50" />
<div class="flex-1 min-w-0">
<div class="text-sm truncate">{entry.client_name}</div>
<progress
class="progress progress-primary w-full h-1"
value={entry.progress}
max="100"
>
</progress>
</div>
<button
type="button"
phx-click="cancel_upload"
phx-value-ref={entry.ref}
class="btn btn-ghost btn-xs"
>
<.icon name="hero-x-mark" class="w-4 h-4" />
</button>
</div>
<% end %>
<%!-- Upload Errors --%>
<%= for {_ref, msg} <- @uploads.comment_attachments.errors do %>
<div class="alert alert-error">
<.icon name="hero-exclamation-circle" class="w-5 h-5" />
<span>
<%= case msg do %>
<% :too_large -> %>
{gettext("File is too large")}
<% :too_many_files -> %>
{gettext("Too many files")}
<% :not_accepted -> %>
{gettext("File type not accepted")}
<% _ -> %>
{inspect(msg)}
<% end %>
</span>
</div>
<% end %>
<%!-- Attach Button --%>
<div class="flex items-center gap-2">
<label class="btn btn-ghost btn-sm gap-2 cursor-pointer">
<.icon name="hero-paper-clip" class="w-4 h-4" />
{gettext("Attach file")}
<.live_file_input upload={@uploads.comment_attachments} class="hidden" />
</label>
<span class="text-xs text-base-content/50">
{gettext("Max 3 files, 10MB each")}
</span>
</div>
<% end %>
<div class="flex justify-end">
<button type="submit" class="btn btn-primary">
<.icon name="hero-paper-airplane" class="w-5 h-5" />
{gettext("Send Comment")}
</button>
</div>
</form>
</div>
</div>
</div>
<%!-- Sidebar --%>
<div class="space-y-6">
<%!-- Status Card --%>
<div class="card bg-base-100 shadow">
<div class="card-body">
<h3 class="font-semibold mb-4">{gettext("Ticket Details")}</h3>
<%!-- Status --%>
<div class="mb-4">
<span class="text-sm text-base-content/70">{gettext("Status")}</span>
<div class="mt-1">
<%= case @ticket.status do %>
<% "open" -> %>
<span class="badge badge-info">{gettext("Open")}</span>
<% "in_progress" -> %>
<span class="badge badge-warning">{gettext("In Progress")}</span>
<% "resolved" -> %>
<span class="badge badge-success">{gettext("Resolved")}</span>
<% "closed" -> %>
<span class="badge">{gettext("Closed")}</span>
<% end %>
</div>
</div>
<%!-- Assigned To --%>
<%= if @ticket.assigned_to do %>
<div class="mb-4">
<span class="text-sm text-base-content/70">{gettext("Assigned To")}</span>
<div class="mt-1 flex items-center gap-2">
<.user_avatar user={@ticket.assigned_to} size="sm" />
<span class="text-sm">{@ticket.assigned_to.email}</span>
</div>
</div>
<% else %>
<div class="mb-4">
<span class="text-sm text-base-content/70">{gettext("Assigned To")}</span>
<div class="mt-1 badge badge-ghost">{gettext("Unassigned")}</div>
</div>
<% end %>
<%!-- Created --%>
<div class="mb-4">
<span class="text-sm text-base-content/70">{gettext("Created")}</span>
<div class="mt-1 text-sm">
{Calendar.strftime(@ticket.inserted_at, "%B %d, %Y at %H:%M")}
</div>
</div>
<%!-- Comments Count --%>
<div class="mb-4">
<span class="text-sm text-base-content/70">{gettext("Comments")}</span>
<div class="mt-1 font-medium">{@ticket.comment_count || 0}</div>
</div>
<%!-- Status Message --%>
<%= if @ticket.status == "resolved" do %>
<div class="alert alert-success">
<.icon name="hero-check-circle" class="w-5 h-5" />
<span>{gettext("This ticket has been resolved.")}</span>
</div>
<% end %>
<%= if @ticket.status == "closed" do %>
<div class="alert">
<.icon name="hero-archive-box" class="w-5 h-5" />
<span>{gettext("This ticket has been closed.")}</span>
</div>
<% end %>
</div>
</div>
<%!-- Help Card --%>
<div class="card bg-base-200">
<div class="card-body">
<h3 class="font-semibold mb-2">{gettext("Need more help?")}</h3>
<p class="text-sm text-base-content/70">
{gettext("Add a comment above to continue the conversation with our support team.")}
</p>
</div>
</div>
</div>
</div>
</div>
</PhoenixKitWeb.Layouts.dashboard>