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

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

<PhoenixKitWeb.Components.LayoutWrapper.app_layout
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title="{@project_title} Invoice {@invoice.invoice_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/invoices")}
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">{@invoice.invoice_number}</h1>
<.invoice_status_badge status={@invoice.status} size={:md} />
</div>
<p class="text-base-content/60 mt-1">
Created <.time_ago datetime={@invoice.inserted_at} />
</p>
</div>
</div>
<div class="flex gap-2 flex-wrap">
<%!-- Send/Resend Invoice - available for all except void --%>
<%= if @invoice.status != "void" do %>
<button phx-click="open_send_modal" class="btn btn-info btn-sm">
<.icon name="hero-paper-airplane" class="w-4 h-4" />
{if @invoice.status == "draft", do: "Send Invoice", else: "Resend Invoice"}
</button>
<% end %>
<%!-- Mark as Paid - available for sent/overdue --%>
<%= if @invoice.status in ["sent", "overdue"] do %>
<button phx-click="open_payment_modal" class="btn btn-success btn-sm">
<.icon name="hero-banknotes" class="w-4 h-4" /> Record Payment
</button>
<% end %>
<%!-- Online Payment Providers - available for sent/overdue with remaining balance --%>
<%= if @invoice.status in ["sent", "overdue"] && Decimal.positive?(PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice)) do %>
<%= for provider <- @available_providers do %>
<button
phx-click="pay_with_provider"
phx-value-provider={provider}
class={[
"btn btn-sm",
provider == :stripe && "btn-primary",
provider == :paypal && "btn-info",
provider == :razorpay && "btn-secondary"
]}
disabled={@checkout_loading != nil}
>
<%= if @checkout_loading == provider do %>
<span class="loading loading-spinner loading-xs"></span>
<% else %>
<%= case provider do %>
<% :stripe -> %>
<.icon name="hero-credit-card" class="w-4 h-4" />
<% :paypal -> %>
<.icon name="hero-currency-dollar" class="w-4 h-4" />
<% :razorpay -> %>
<.icon name="hero-banknotes" class="w-4 h-4" />
<% _ -> %>
<.icon name="hero-credit-card" class="w-4 h-4" />
<% end %>
<% end %>
Pay with {provider |> Atom.to_string() |> String.capitalize()}
</button>
<% end %>
<% end %>
<%!-- Issue Refund - available if has payments --%>
<%= if PhoenixKit.Modules.Billing.Invoice.has_payments?(@invoice) do %>
<button phx-click="open_refund_modal" class="btn btn-warning btn-sm">
<.icon name="hero-arrow-uturn-left" class="w-4 h-4" /> Issue Refund
</button>
<% end %>
<%!-- Generate Receipt - available when paid_amount > 0 and no receipt yet --%>
<%= if is_nil(@invoice.receipt_number) && @invoice.paid_amount && Decimal.gt?(@invoice.paid_amount, Decimal.new(0)) do %>
<button phx-click="generate_receipt" class="btn btn-outline btn-sm">
<.icon name="hero-document-check" class="w-4 h-4" />
<%= if @invoice.status == "paid" do %>
Generate Receipt
<% else %>
Generate Partial Receipt
<% end %>
</button>
<% end %>
<%!-- Void - available for draft/sent/overdue --%>
<%= if @invoice.status in ["draft", "sent", "overdue"] do %>
<button
phx-click="void_invoice"
class="btn btn-error btn-outline btn-sm"
data-confirm="Are you sure you want to void this invoice?"
>
<.icon name="hero-x-mark" class="w-4 h-4" /> Void
</button>
<% end %>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/invoices/#{@invoice.id}/print")}
class="btn btn-outline btn-sm"
target="_blank"
>
<.icon name="hero-printer" class="w-4 h-4" /> Print / PDF
</.link>
<%!-- View Receipt - available when receipt is generated --%>
<%= if @invoice.receipt_number do %>
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/billing/invoices/#{@invoice.id}/receipt")
}
class="btn btn-success btn-sm"
target="_blank"
>
<.icon name="hero-document-check" class="w-4 h-4" /> View Receipt
</.link>
<button phx-click="open_send_receipt_modal" class="btn btn-success btn-outline btn-sm">
<.icon name="hero-paper-airplane" class="w-4 h-4" /> Send Receipt
</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">
<%!-- Invoice Details --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Invoice Details</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 <- @invoice.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={@invoice.currency}
/>
</td>
<td class="text-right">
<.currency_compact amount={item["total"]} currency={@invoice.currency} />
</td>
</tr>
<% end %>
</tbody>
<tfoot>
<tr>
<td colspan="3" class="text-right font-medium">Subtotal</td>
<td class="text-right">
<.currency_compact amount={@invoice.subtotal} currency={@invoice.currency} />
</td>
</tr>
<%= if Decimal.gt?(@invoice.tax_amount || Decimal.new(0), Decimal.new(0)) do %>
<tr>
<td colspan="3" class="text-right">
Tax ({Decimal.round(
Decimal.mult(@invoice.tax_rate || Decimal.new(0), 100),
2
)
|> Decimal.normalize()
|> Decimal.to_string()}%)
</td>
<td class="text-right">
<.currency_compact
amount={@invoice.tax_amount}
currency={@invoice.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={@invoice.total} currency={@invoice.currency} />
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<%!-- Payments & Transactions --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<div class="flex justify-between items-center">
<h2 class="card-title">Payments & Transactions</h2>
<.link
navigate={PhoenixKit.Utils.Routes.path("/admin/billing/transactions")}
class="btn btn-ghost btn-xs"
>
View All <.icon name="hero-arrow-right" class="w-3 h-3" />
</.link>
</div>
<%!-- Payment Summary --%>
<div class="grid grid-cols-3 gap-4 mt-4">
<div class="bg-base-200 rounded-lg p-4 text-center">
<div class="text-sm text-base-content/60">Total</div>
<div class="text-lg font-bold">
<.currency_compact amount={@invoice.total} currency={@invoice.currency} />
</div>
</div>
<div class="bg-success/10 rounded-lg p-4 text-center">
<div class="text-sm text-success">Paid</div>
<div class="text-lg font-bold text-success">
<.currency_compact amount={@invoice.paid_amount} currency={@invoice.currency} />
</div>
</div>
<div class={[
"rounded-lg p-4 text-center",
if(
Decimal.positive?(
PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice)
),
do: "bg-warning/10",
else: "bg-base-200"
)
]}>
<div class={[
"text-sm",
if(
Decimal.positive?(
PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice)
),
do: "text-warning",
else: "text-base-content/60"
)
]}>
Remaining
</div>
<div class={[
"text-lg font-bold",
if(
Decimal.positive?(
PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice)
),
do: "text-warning",
else: "text-base-content/60"
)
]}>
<.currency_compact
amount={PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice)}
currency={@invoice.currency}
/>
</div>
</div>
</div>
<%!-- Transactions Table --%>
<%= if Enum.empty?(@transactions) do %>
<div class="text-center py-6 text-base-content/50">
<.icon name="hero-banknotes" class="w-8 h-8 mx-auto mb-2 opacity-50" />
<p>No transactions recorded yet</p>
</div>
<% else %>
<div class="overflow-x-auto mt-4">
<table class="table table-sm">
<thead>
<tr>
<th>Date</th>
<th>Number</th>
<th>Type</th>
<th class="text-right">Amount</th>
<th>Description</th>
<th class="text-right">Actions</th>
</tr>
</thead>
<tbody>
<%= for transaction <- @transactions do %>
<tr>
<td class="text-sm text-base-content/60">
<.time_ago datetime={transaction.inserted_at} />
</td>
<td class="font-mono text-xs">{transaction.transaction_number}</td>
<td>
<.transaction_type_badge type={
PhoenixKit.Modules.Billing.Transaction.type(transaction)
} />
</td>
<td class="text-right">
<span class={[
"font-mono font-medium",
if(Decimal.positive?(transaction.amount),
do: "text-success",
else: "text-error"
)
]}>
<%= if Decimal.positive?(transaction.amount) do %>
+<.currency_compact
amount={transaction.amount}
currency={transaction.currency}
/>
<% else %>
<.currency_compact
amount={transaction.amount}
currency={transaction.currency}
/>
<% end %>
</span>
</td>
<td class="max-w-[200px] truncate text-sm text-base-content/70">
{transaction.description || "-"}
</td>
<td class="text-right">
<div class="flex gap-1 justify-end">
<%= if Decimal.negative?(transaction.amount) do %>
<%!-- Refund: Credit Note buttons --%>
<.link
navigate={
PhoenixKit.Utils.Routes.path(
"/admin/billing/invoices/#{@invoice.id}/credit-note/#{transaction.id}"
)
}
class="btn btn-warning btn-xs"
target="_blank"
title="Print Credit Note"
>
<.icon name="hero-printer" class="w-3 h-3" />
</.link>
<button
phx-click="open_send_credit_note_modal"
phx-value-transaction-id={transaction.id}
class="btn btn-warning btn-outline btn-xs"
title="Send Credit Note"
>
<.icon name="hero-paper-airplane" class="w-3 h-3" />
</button>
<% else %>
<%!-- Payment: Payment Confirmation buttons --%>
<.link
navigate={
PhoenixKit.Utils.Routes.path(
"/admin/billing/invoices/#{@invoice.id}/payment/#{transaction.id}"
)
}
class="btn btn-success btn-xs"
target="_blank"
title="Print Payment Confirmation"
>
<.icon name="hero-printer" class="w-3 h-3" />
</.link>
<button
phx-click="open_send_payment_confirmation_modal"
phx-value-transaction-id={transaction.id}
class="btn btn-success btn-outline btn-xs"
title="Send Payment Confirmation"
>
<.icon name="hero-paper-airplane" class="w-3 h-3" />
</button>
<% end %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</div>
</div>
<%!-- Payment Terms & Notes --%>
<%= if @invoice.payment_terms || @invoice.notes do %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Payment Information</h2>
<%= if @invoice.payment_terms do %>
<div class="mt-2">
<h4 class="font-medium text-sm text-base-content/60">Payment Terms</h4>
<p class="mt-1">{@invoice.payment_terms}</p>
</div>
<% end %>
<%= if @invoice.bank_details && map_size(@invoice.bank_details) > 0 do %>
<div class="mt-4">
<h4 class="font-medium text-sm text-base-content/60">Bank Details</h4>
<div class="mt-2 bg-base-200 p-4 rounded-lg font-mono text-sm">
<%= if @invoice.bank_details["bank_name"] do %>
<div>Bank: {@invoice.bank_details["bank_name"]}</div>
<% end %>
<%= if @invoice.bank_details["account_name"] do %>
<div>Account: {@invoice.bank_details["account_name"]}</div>
<% end %>
<%= if @invoice.bank_details["iban"] do %>
<div>IBAN: {@invoice.bank_details["iban"]}</div>
<% end %>
<%= if @invoice.bank_details["swift"] do %>
<div>SWIFT/BIC: {@invoice.bank_details["swift"]}</div>
<% end %>
</div>
</div>
<% end %>
<%= if @invoice.notes do %>
<div class="mt-4">
<h4 class="font-medium text-sm text-base-content/60">Notes</h4>
<p class="mt-1">{@invoice.notes}</p>
</div>
<% end %>
</div>
</div>
<% end %>
<%!-- Receipt --%>
<%= if @invoice.receipt_number do %>
<div class="card bg-success/10 border border-success shadow-xl">
<div class="card-body">
<div class="flex items-center gap-3">
<.icon name="hero-document-check" class="w-8 h-8 text-success" />
<div>
<h2 class="card-title text-success">Receipt Generated</h2>
<p class="font-mono">{@invoice.receipt_number}</p>
<p class="text-sm text-base-content/60 mt-1">
Generated <.time_ago datetime={@invoice.receipt_generated_at} />
</p>
</div>
</div>
</div>
</div>
<% end %>
</div>
<%!-- Sidebar --%>
<div class="space-y-6">
<%!-- Related Order --%>
<%= if @invoice.order do %>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Related Order</h2>
<div class="mt-2">
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/billing/orders/#{@invoice.order.id}")
}
class="flex items-center gap-3 p-3 bg-base-200 rounded-lg hover:bg-base-300 transition-colors"
>
<.icon name="hero-clipboard-document-list" class="w-6 h-6 text-primary" />
<div>
<div class="font-mono font-medium">{@invoice.order.order_number}</div>
<div class="text-sm text-base-content/60">
<.order_status_badge status={@invoice.order.status} size={:xs} />
</div>
</div>
</.link>
</div>
</div>
</div>
<% end %>
<%!-- Customer Info --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Customer</h2>
<%= if @invoice.user do %>
<div class="flex items-center gap-3 mt-2">
<.user_avatar user={@invoice.user} size="lg" />
<div>
<div class="font-medium">{@invoice.user.email}</div>
<.link
navigate={
PhoenixKit.Utils.Routes.path("/admin/users/edit/#{@invoice.user.id}")
}
class="text-sm text-primary hover:underline"
>
View Profile
</.link>
</div>
</div>
<% else %>
<p class="text-base-content/50 mt-2">No customer linked</p>
<% end %>
</div>
</div>
<%!-- Billing Details --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Billing Details</h2>
<%= if @invoice.billing_details && map_size(@invoice.billing_details) > 0 do %>
<div class="mt-2 space-y-2 text-sm">
<%= if @invoice.billing_details["type"] == "company" do %>
<div class="font-medium">{@invoice.billing_details["company_name"]}</div>
<%= if @invoice.billing_details["company_vat_number"] do %>
<div class="text-base-content/60">
VAT: {@invoice.billing_details["company_vat_number"]}
</div>
<% end %>
<% else %>
<div class="font-medium">
{@invoice.billing_details["first_name"]} {@invoice.billing_details[
"last_name"
]}
</div>
<% end %>
<%= if @invoice.billing_details["address_line1"] do %>
<div class="text-base-content/60">
{@invoice.billing_details["address_line1"]}<br />
<%= if @invoice.billing_details["address_line2"] do %>
{@invoice.billing_details["address_line2"]}<br />
<% end %>
{@invoice.billing_details["city"]}, {@invoice.billing_details["postal_code"]}<br />
{@invoice.billing_details["country"]}
</div>
<% end %>
</div>
<% else %>
<p class="text-base-content/50 mt-2">No billing details</p>
<% end %>
</div>
</div>
<%!-- Timeline (sorted by datetime) --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title">Timeline</h2>
<% timeline_events = build_timeline_events(@invoice, @transactions) %>
<ul class="timeline timeline-vertical mt-2">
<%= for {event, index} <- Enum.with_index(timeline_events) do %>
<li>
<%= if index > 0 do %>
<hr />
<% end %>
<%= case event.type do %>
<% :created -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle">
<.icon name="hero-document-plus" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">Created</div>
<% :invoice_sent -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-info">
<.icon name="hero-paper-airplane" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">
<div>Invoice Sent</div>
<div class="text-xs text-base-content/60">{event.data["email"]}</div>
</div>
<% :invoice_sent_legacy -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-info">
<.icon name="hero-paper-airplane" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">Invoice Sent</div>
<% :payment -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-success">
<.icon name="hero-banknotes" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box bg-success/10 border-success">
<div>Payment</div>
<div class="text-xs font-mono">
+<.currency_compact
amount={event.data.amount}
currency={event.data.currency}
/>
</div>
<%= if event.data.description do %>
<div class="text-xs text-base-content/60">{event.data.description}</div>
<% end %>
</div>
<% :paid -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-success">
<.icon name="hero-check-circle" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box bg-success/10 border-success">
Fully Paid
</div>
<% :receipt_generated -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-success">
<.icon name="hero-document-check" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">
<div>Receipt Generated</div>
<div class="text-xs text-base-content/60 font-mono">{event.data}</div>
</div>
<% :receipt_sent -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-success">
<.icon name="hero-paper-airplane" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">
<div>Receipt Sent</div>
<div class="text-xs text-base-content/60">{event.data["email"]}</div>
</div>
<% :refund -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-warning">
<.icon name="hero-arrow-uturn-left" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box bg-warning/10 border-warning">
<div>Refund</div>
<div class="text-xs font-mono">
<.currency_compact
amount={Decimal.abs(event.data.amount)}
currency={event.data.currency}
/>
</div>
<%= if event.data.description do %>
<div class="text-xs text-base-content/60">{event.data.description}</div>
<% end %>
</div>
<% :credit_note_sent -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-warning">
<.icon name="hero-paper-airplane" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">
<div>Credit Note Sent</div>
<div class="text-xs text-base-content/60">{event.data["email"]}</div>
<div class="text-xs text-base-content/60 font-mono">
{event.data["credit_note_number"]}
</div>
</div>
<% :voided -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle text-error">
<.icon name="hero-x-circle" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box bg-error/10 border-error">Voided</div>
<% _ -> %>
<div class="timeline-start text-sm text-base-content/60">
<.time_ago datetime={event.datetime} />
</div>
<div class="timeline-middle">
<.icon name="hero-question-mark-circle" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box">Unknown Event</div>
<% end %>
<%= if index < length(timeline_events) - 1 do %>
<hr />
<% end %>
</li>
<% end %>
<%!-- Full refund indicator (always at the end if applicable) --%>
<%= if fully_refunded?(@invoice, @transactions) do %>
<li>
<hr />
<div class="timeline-start text-sm text-base-content/60"></div>
<div class="timeline-middle text-error">
<.icon name="hero-x-circle" class="w-4 h-4" />
</div>
<div class="timeline-end timeline-box bg-error/10 border-error">
<div>Fully Refunded</div>
</div>
</li>
<% end %>
</ul>
</div>
</div>
</div>
</div>
</div>
<%!-- Payment Modal --%>
<%= if @show_payment_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">Record Payment</h3>
<p class="py-2 text-base-content/60">
Recording payment for invoice {@invoice.invoice_number}
</p>
<form phx-change="update_payment_form" phx-submit="record_payment">
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Amount ({@invoice.currency})</span>
<span class="label-text-alt">
Remaining:
<.currency_compact
amount={PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice)}
currency={@invoice.currency}
/>
</span>
</label>
<input
type="number"
name="amount"
step="0.01"
min="0.01"
max={
Decimal.to_string(PhoenixKit.Modules.Billing.Invoice.remaining_amount(@invoice))
}
value={@payment_amount}
class="input input-bordered"
required
/>
</div>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Payment Method</span>
</label>
<select name="payment_method" class="select select-bordered">
<%= for method <- @available_payment_methods do %>
<option value={method} selected={method == @selected_payment_method}>
{format_payment_method_name(method)}
</option>
<% end %>
</select>
</div>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Description (optional)</span>
</label>
<input
type="text"
name="description"
value={@payment_description}
placeholder="Payment reference, notes..."
class="input input-bordered"
/>
</div>
<div class="modal-action">
<button type="button" phx-click="close_payment_modal" class="btn">Cancel</button>
<button type="submit" class="btn btn-success">
<.icon name="hero-banknotes" class="w-4 h-4" /> Record Payment
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="close_payment_modal"></div>
</div>
<% end %>
<%!-- Refund Modal --%>
<%= if @show_refund_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">Issue Refund</h3>
<p class="py-2 text-base-content/60">
Issue refund for invoice {@invoice.invoice_number}
</p>
<form phx-change="update_refund_form" phx-submit="record_refund">
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Refund Amount ({@invoice.currency})</span>
<span class="label-text-alt">
Max:
<.currency_compact
amount={@invoice.paid_amount}
currency={@invoice.currency}
/>
</span>
</label>
<input
type="number"
name="amount"
step="0.01"
min="0.01"
max={Decimal.to_string(@invoice.paid_amount)}
value={@refund_amount}
class="input input-bordered"
placeholder="Enter refund amount"
required
/>
</div>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Reason <span class="text-error">*</span></span>
</label>
<input
type="text"
name="description"
value={@refund_description}
placeholder="Reason for refund (required)"
class="input input-bordered"
required
/>
</div>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Refund Method</span>
</label>
<select name="payment_method" class="select select-bordered">
<%= for method <- @available_payment_methods do %>
<option value={method} selected={method == @selected_refund_payment_method}>
{format_payment_method_name(method)}
</option>
<% end %>
</select>
</div>
<div class="modal-action">
<button type="button" phx-click="close_refund_modal" class="btn">Cancel</button>
<button type="submit" class="btn btn-warning">
<.icon name="hero-arrow-uturn-left" class="w-4 h-4" /> Issue Refund
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="close_refund_modal"></div>
</div>
<% end %>
<%!-- Send Invoice Modal --%>
<%= if @show_send_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">
{if @invoice.status == "draft", do: "Send Invoice", else: "Resend Invoice"}
</h3>
<p class="py-2 text-base-content/60">
{if @invoice.status == "draft",
do: "Send invoice to customer's email address",
else: "Resend invoice to customer's email address"}
</p>
<form phx-change="update_send_form" phx-submit="send_invoice">
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Email Address</span>
</label>
<input
type="email"
name="email"
value={@send_email}
placeholder="customer@example.com"
class="input input-bordered"
required
/>
</div>
<div class="modal-action">
<button type="button" phx-click="close_send_modal" class="btn">Cancel</button>
<button type="submit" class="btn btn-info">
<.icon name="hero-paper-airplane" class="w-4 h-4" />
{if @invoice.status == "draft", do: "Send Invoice", else: "Resend Invoice"}
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="close_send_modal"></div>
</div>
<% end %>
<%!-- Send Receipt Modal --%>
<%= if @show_send_receipt_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">Send Receipt</h3>
<p class="py-2 text-base-content/60">
Send payment receipt to customer's email address
</p>
<form phx-change="update_send_receipt_form" phx-submit="send_receipt">
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Email Address</span>
</label>
<input
type="email"
name="email"
value={@send_receipt_email}
placeholder="customer@example.com"
class="input input-bordered"
required
/>
</div>
<div class="modal-action">
<button type="button" phx-click="close_send_receipt_modal" class="btn">Cancel</button>
<button type="submit" class="btn btn-success">
<.icon name="hero-paper-airplane" class="w-4 h-4" /> Send Receipt
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="close_send_receipt_modal"></div>
</div>
<% end %>
<%!-- Send Credit Note Modal --%>
<%= if @show_send_credit_note_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">Send Credit Note</h3>
<p class="py-2 text-base-content/60">
Send credit note (refund document) to customer's email address
</p>
<form phx-change="update_send_credit_note_form" phx-submit="send_credit_note">
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Email Address</span>
</label>
<input
type="email"
name="email"
value={@send_credit_note_email}
placeholder="customer@example.com"
class="input input-bordered"
required
/>
</div>
<div class="modal-action">
<button type="button" phx-click="close_send_credit_note_modal" class="btn">
Cancel
</button>
<button type="submit" class="btn btn-warning">
<.icon name="hero-paper-airplane" class="w-4 h-4" /> Send Credit Note
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="close_send_credit_note_modal"></div>
</div>
<% end %>
<%!-- Send Payment Confirmation Modal --%>
<%= if @show_send_payment_confirmation_modal do %>
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">Send Payment Confirmation</h3>
<p class="py-2 text-base-content/60">
Send payment confirmation to customer's email address
</p>
<form
phx-change="update_send_payment_confirmation_form"
phx-submit="send_payment_confirmation"
>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Email Address</span>
</label>
<input
type="email"
name="email"
value={@send_payment_confirmation_email}
placeholder="customer@example.com"
class="input input-bordered"
required
/>
</div>
<div class="modal-action">
<button type="button" phx-click="close_send_payment_confirmation_modal" class="btn">
Cancel
</button>
<button type="submit" class="btn btn-success">
<.icon name="hero-paper-airplane" class="w-4 h-4" /> Send Confirmation
</button>
</div>
</form>
</div>
<div class="modal-backdrop" phx-click="close_send_payment_confirmation_modal"></div>
</div>
<% end %>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>