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 billing web order_detail.html.heex
Raw

lib/modules/billing/web/order_detail.html.heex

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="{@project_title} Order {@order.order_number}"
current_path={@url_path}
project_title={@project_title}
>
<div class="container mx-auto px-4 py-6">
<%!-- Header --%>
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 gap-4">
<div class="flex items-center gap-4">
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/orders")}
class="btn btn-ghost btn-sm"
>
<.icon name="hero-arrow-left" class="w-4 h-4" />
</.link>
<div>
<div class="flex items-center gap-3">
<h1 class="text-3xl font-bold font-mono">{@order.order_number}</h1>
<.order_status_badge status={@order.status} size={:md} />
</div>
<p class="text-base-content/60 mt-1">
Created <.time_ago datetime={@order.inserted_at} />
</p>
</div>
</div>
<div class="flex gap-2">
<%= case @order.status do %>
<% "draft" -> %>
<button phx-click="confirm_order" class="btn btn-info btn-sm">
<.icon name="hero-check" class="w-4 h-4" /> Confirm Order
</button>
<button phx-click="generate_invoice" class="btn btn-outline btn-sm">
<.icon name="hero-document-text" class="w-4 h-4" /> Generate Invoice
</button>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/orders/#{@order.id}/edit")}
class="btn btn-outline btn-sm"
>
<.icon name="hero-pencil" class="w-4 h-4" /> Edit
</.link>
<% "pending" -> %>
<button phx-click="confirm_order" class="btn btn-info btn-sm">
<.icon name="hero-check" class="w-4 h-4" /> Confirm Order
</button>
<button phx-click="generate_invoice" class="btn btn-outline btn-sm">
<.icon name="hero-document-text" class="w-4 h-4" /> Generate Invoice
</button>
<% "confirmed" -> %>
<button phx-click="mark_paid" class="btn btn-success btn-sm">
<.icon name="hero-banknotes" class="w-4 h-4" /> Mark as Paid
</button>
<button phx-click="generate_invoice" class="btn btn-outline btn-sm">
<.icon name="hero-document-text" class="w-4 h-4" /> Generate Invoice
</button>
<% _ -> %>
<% end %>
<%= if @order.status in ["draft", "pending", "confirmed"] do %>
<button
phx-click="cancel_order"
class="btn btn-error btn-outline btn-sm"
data-confirm="Are you sure you want to cancel this order?"
>
<.icon name="hero-x-mark" class="w-4 h-4" /> Cancel
</button>
<% end %>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<%!-- Main Content --%>
<div class="lg:col-span-2 space-y-6">
<%!-- Order Summary --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Order Summary</h2>
<%!-- Line Items --%>
<div class="overflow-x-auto mt-4">
<table class="table">
<thead>
<tr>
<th>Item</th>
<th class="text-right">Qty</th>
<th class="text-right">Unit Price</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
<%= for item <- @order.line_items || [] do %>
<tr>
<td>
<div class="font-medium">{item["name"]}</div>
<%= if item["description"] do %>
<div class="text-sm text-base-content/60">{item["description"]}</div>
<% end %>
</td>
<td class="text-right">{item["quantity"]}</td>
<td class="text-right">
<.currency_compact amount={item["unit_price"]} currency={@order.currency} />
</td>
<td class="text-right">
<.currency_compact amount={item["total"]} currency={@order.currency} />
</td>
</tr>
<% end %>
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right font-medium">Subtotal</td>
<td class="text-right">
<.currency_compact amount={@order.subtotal} currency={@order.currency} />
</td>
</tr>
<%= if Decimal.gt?(@order.tax_amount || Decimal.new(0), Decimal.new(0)) do %>
<tr>
<td colspan="3" class="text-right">
Tax ({Decimal.round(
Decimal.mult(@order.tax_rate || Decimal.new(0), 100),
2
)
|> Decimal.normalize()
|> Decimal.to_string()}%)
</td>
<td class="text-right">
<.currency_compact amount={@order.tax_amount} currency={@order.currency} />
</td>
</tr>
<% end %>
<%= if Decimal.gt?(@order.discount_amount || Decimal.new(0), Decimal.new(0)) do %>
<tr>
<td colspan="3" class="text-right text-success">
Discount
<%= if @order.discount_code do %>
<span class="badge badge-sm badge-outline ml-1">
{@order.discount_code}
</span>
<% end %>
</td>
<td class="text-right text-success">
-<.currency_compact
amount={@order.discount_amount}
currency={@order.currency}
/>
</td>
</tr>
<% end %>
<tr class="text-lg font-bold">
<td colspan="3" class="text-right">Total</td>
<td class="text-right">
<.currency_amount amount={@order.total} currency={@order.currency} />
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<%!-- Notes --%>
<%= if @order.notes || @order.internal_notes do %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Notes</h2>
<%= if @order.notes do %>
<div class="mt-2">
<h4 class="font-medium text-sm text-base-content/60">Customer Notes</h4>
<p class="mt-1">{@order.notes}</p>
</div>
<% end %>
<%= if @order.internal_notes do %>
<div class="mt-4">
<h4 class="font-medium text-sm text-base-content/60">Internal Notes</h4>
<p class="mt-1 bg-warning/10 p-3 rounded-lg">{@order.internal_notes}</p>
</div>
<% end %>
</div>
</div>
<% end %>
<%!-- Invoices --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex justify-between items-center">
<h2 class="card-title">Invoices</h2>
<%= if @order.status in ["draft", "pending", "confirmed"] do %>
<button phx-click="generate_invoice" class="btn btn-outline btn-sm">
<.icon name="hero-plus" class="w-4 h-4" /> Generate Invoice
</button>
<% end %>
</div>
<%= if Enum.empty?(@invoices) do %>
<div class="text-center py-8 text-base-content/50">
<.icon name="hero-document-text" class="w-12 h-12 mx-auto mb-2 opacity-50" />
<p>No invoices generated yet</p>
</div>
<% else %>
<div class="overflow-x-auto mt-4">
<table class="table table-sm">
<thead>
<tr>
<th>Invoice #</th>
<th>Status</th>
<th>Total</th>
<th>Due Date</th>
<th></th>
</tr>
</thead>
<tbody>
<%= for invoice <- @invoices do %>
<tr
class="hover cursor-pointer"
phx-click="view_invoice"
phx-value-id={invoice.id}
>
<td class="font-mono">{invoice.invoice_number}</td>
<td><.invoice_status_badge status={invoice.status} /></td>
<td>
<.currency_compact amount={invoice.total} currency={invoice.currency} />
</td>
<td>
<%= if invoice.due_date do %>
{Calendar.strftime(invoice.due_date, "%b %d, %Y")}
<% else %>
<span class="text-base-content/40">-</span>
<% end %>
</td>
<td>
<.link
navigate={
PhoenixKit.Utils.Routes.path(
"/admin/billing/invoices/#{invoice.id}"
)
}
class="btn btn-ghost btn-xs"
>
<.icon name="hero-eye" class="w-4 h-4" />
</.link>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</div>
</div>
</div>
<%!-- Sidebar --%>
<div class="space-y-6">
<%!-- Customer Info --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Customer</h2>
<%= if @order.user do %>
<div class="flex items-center gap-3 mt-2">
<.user_avatar user={@order.user} size="lg" />
<div>
<div class="font-medium">{@order.user.email}</div>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/users/#{@order.user.id}")}
class="text-sm text-primary hover:underline"
>
View Profile
</.link>
</div>
</div>
<% else %>
<p class="text-base-content/50">No customer linked</p>
<% end %>
</div>
</div>
<%!-- Billing Info --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Billing Information</h2>
<%= if @order.billing_snapshot && map_size(@order.billing_snapshot) > 0 do %>
<div class="mt-2 space-y-2 text-sm">
<%= if @order.billing_snapshot["type"] == "company" do %>
<div class="font-medium">{@order.billing_snapshot["company_name"]}</div>
<%= if @order.billing_snapshot["company_vat_number"] do %>
<div class="text-base-content/60">
VAT: {@order.billing_snapshot["company_vat_number"]}
</div>
<% end %>
<% else %>
<div class="font-medium">
{@order.billing_snapshot["first_name"]} {@order.billing_snapshot["last_name"]}
</div>
<% end %>
<%= if @order.billing_snapshot["address_line1"] do %>
<div class="text-base-content/60">
{@order.billing_snapshot["address_line1"]}<br />
<%= if @order.billing_snapshot["address_line2"] do %>
{@order.billing_snapshot["address_line2"]}<br />
<% end %>
{@order.billing_snapshot["city"]}, {@order.billing_snapshot["postal_code"]}<br />
{@order.billing_snapshot["country"]}
</div>
<% end %>
</div>
<% else %>
<p class="text-base-content/50 mt-2">No billing information</p>
<%= if @order.billing_profile do %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/profiles")}
class="btn btn-outline btn-sm mt-2"
>
View Billing Profile
</.link>
<% end %>
<% end %>
</div>
</div>
<%!-- Payment Details --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Payment</h2>
<div class="mt-2 space-y-3">
<div class="flex justify-between">
<span class="text-base-content/60">Method</span>
<%= if @order.payment_method do %>
<span class="badge badge-outline">{String.upcase(@order.payment_method)}</span>
<% else %>
<span class="text-base-content/40 text-sm">Not specified</span>
<% end %>
</div>
<div class="flex justify-between">
<span class="text-base-content/60">Currency</span>
<.currency_badge code={@order.currency} size={:sm} />
</div>
<%= if @order.confirmed_at do %>
<div class="flex justify-between">
<span class="text-base-content/60">Confirmed</span>
<span class="text-sm">
<.time_ago datetime={@order.confirmed_at} />
</span>
</div>
<% end %>
<%= if @order.paid_at do %>
<div class="flex justify-between">
<span class="text-base-content/60">Paid</span>
<span class="text-sm">
<.time_ago datetime={@order.paid_at} />
</span>
</div>
<% end %>
<%= if @order.cancelled_at do %>
<div class="flex justify-between">
<span class="text-base-content/60">Cancelled</span>
<span class="text-sm text-error">
<.time_ago datetime={@order.cancelled_at} />
</span>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>