Files
MySMSAPio/app/views/admin/logs/index.html.erb
Min Zeya Phyo 1fdff55544
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
SMS gateway api service for android application
2026-01-27 11:24:16 +06:30

299 lines
16 KiB
Plaintext

<div class="space-y-6">
<!-- Page header -->
<div class="border-b border-gray-200 pb-5">
<h1 class="text-3xl font-bold leading-tight tracking-tight text-gray-900">SMS Logs</h1>
<p class="mt-2 text-sm text-gray-600">View and filter all SMS messages across your gateways.</p>
</div>
<!-- Filters card -->
<div class="rounded-xl bg-white shadow-sm ring-1 ring-gray-900/5 px-6 py-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">Filters</h3>
<i class="fas fa-filter text-gray-400"></i>
</div>
<%= form_with url: admin_logs_path, method: :get, local: true do |f| %>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Direction filter -->
<div>
<%= label_tag :direction, "Direction", class: "block text-sm font-medium text-gray-700 mb-1" %>
<%= select_tag :direction,
options_for_select([["All Directions", ""], ["Inbound", "inbound"], ["Outbound", "outbound"]], params[:direction]),
class: "block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
<!-- Status filter -->
<div>
<%= label_tag :status, "Status", class: "block text-sm font-medium text-gray-700 mb-1" %>
<%= select_tag :status,
options_for_select([["All Statuses", ""], ["Pending", "pending"], ["Queued", "queued"], ["Sent", "sent"], ["Delivered", "delivered"], ["Failed", "failed"]], params[:status]),
class: "block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
<!-- Phone number filter -->
<div>
<%= label_tag :phone_number, "Phone Number", class: "block text-sm font-medium text-gray-700 mb-1" %>
<div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<i class="fas fa-phone text-gray-400"></i>
</div>
<%= text_field_tag :phone_number, params[:phone_number],
class: "block w-full pl-10 rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm",
placeholder: "Search phone..." %>
</div>
</div>
<!-- Gateway filter -->
<div>
<%= label_tag :gateway_id, "Gateway", class: "block text-sm font-medium text-gray-700 mb-1" %>
<%= select_tag :gateway_id,
options_for_select([["All Gateways", ""]] + Gateway.order(:name).pluck(:name, :id), params[:gateway_id]),
class: "block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
<!-- Start date filter -->
<div>
<%= label_tag :start_date, "Start Date", class: "block text-sm font-medium text-gray-700 mb-1" %>
<div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<i class="fas fa-calendar text-gray-400"></i>
</div>
<%= date_field_tag :start_date, params[:start_date],
class: "block w-full pl-10 rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
</div>
<!-- End date filter -->
<div>
<%= label_tag :end_date, "End Date", class: "block text-sm font-medium text-gray-700 mb-1" %>
<div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<i class="fas fa-calendar text-gray-400"></i>
</div>
<%= date_field_tag :end_date, params[:end_date],
class: "block w-full pl-10 rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
</div>
</div>
<!-- Filter buttons -->
<div class="flex items-center gap-3 mt-6 pt-4 border-t border-gray-200">
<%= submit_tag "Apply Filters",
class: "inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition-all duration-200" do %>
<i class="fas fa-filter"></i>
Apply Filters
<% end %>
<%= link_to admin_logs_path,
class: "inline-flex items-center gap-2 rounded-lg bg-gray-100 px-4 py-2 text-sm font-semibold text-gray-700 hover:bg-gray-200 transition-all duration-200" do %>
<i class="fas fa-times"></i>
Clear Filters
<% end %>
</div>
<% end %>
</div>
<!-- Messages table card -->
<div class="rounded-xl bg-white shadow-sm ring-1 ring-gray-900/5 overflow-hidden">
<% if @messages.any? %>
<!-- Table header with count -->
<div class="px-6 py-4 border-b border-gray-200 bg-gray-50">
<p class="text-sm text-gray-700">
Showing <span class="font-semibold"><%= @messages.size %></span> of <span class="font-semibold"><%= @pagy.count %></span> messages
</p>
</div>
<!-- Table -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Message ID</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Phone Number</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Message</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Direction</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Status</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Gateway</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Retries</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Created</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500">Processed</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<% @messages.each do |msg| %>
<tr class="hover:bg-gray-50 transition-colors cursor-pointer" onclick="toggleErrorRow('error-<%= msg.id %>')">
<td class="whitespace-nowrap px-6 py-4 text-sm">
<code class="rounded bg-gray-100 px-2 py-1 text-xs font-mono text-gray-800"><%= msg.message_id[0..15] %>...</code>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm font-medium text-gray-900">
<%= msg.phone_number %>
</td>
<td class="px-6 py-4 text-sm text-gray-700">
<div class="max-w-md whitespace-pre-wrap break-words"><%= msg.message_body %></div>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm">
<% if msg.direction == "outbound" %>
<span class="inline-flex items-center gap-1 rounded-full bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10">
<i class="fas fa-arrow-up"></i> Outbound
</span>
<% else %>
<span class="inline-flex items-center gap-1 rounded-full bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-700/10">
<i class="fas fa-arrow-down"></i> Inbound
</span>
<% end %>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm">
<% case msg.status %>
<% when "delivered" %>
<span class="inline-flex rounded-full bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-700/10">Delivered</span>
<% when "sent" %>
<span class="inline-flex rounded-full bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10">Sent</span>
<% when "failed" %>
<span class="inline-flex items-center gap-1 rounded-full bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-700/10">
<i class="fas fa-exclamation-circle"></i> Failed
</span>
<% when "pending" %>
<span class="inline-flex rounded-full bg-yellow-50 px-2 py-1 text-xs font-medium text-yellow-700 ring-1 ring-inset ring-yellow-700/10">Pending</span>
<% when "queued" %>
<span class="inline-flex rounded-full bg-purple-50 px-2 py-1 text-xs font-medium text-purple-700 ring-1 ring-inset ring-purple-700/10">Queued</span>
<% else %>
<span class="inline-flex rounded-full bg-gray-50 px-2 py-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-700/10"><%= msg.status.titleize %></span>
<% end %>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
<%= msg.gateway&.name || "-" %>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm text-center text-gray-500">
<% if msg.retry_count > 0 %>
<span class="inline-flex items-center justify-center rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800">
<%= msg.retry_count %>
</span>
<% else %>
-
<% end %>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
<%= msg.created_at.strftime("%m/%d/%y %H:%M") %>
</td>
<td class="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
<% if msg.delivered_at %>
<span class="text-green-600"><%= msg.delivered_at.strftime("%m/%d/%y %H:%M") %></span>
<% elsif msg.sent_at %>
<span class="text-blue-600"><%= msg.sent_at.strftime("%m/%d/%y %H:%M") %></span>
<% elsif msg.failed_at %>
<span class="text-red-600"><%= msg.failed_at.strftime("%m/%d/%y %H:%M") %></span>
<% else %>
-
<% end %>
</td>
</tr>
<% if msg.error_message.present? %>
<tr id="error-<%= msg.id %>" class="hidden bg-red-50">
<td colspan="9" class="px-6 py-4">
<div class="flex items-start gap-3">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-triangle text-red-600"></i>
</div>
<div>
<p class="text-sm font-semibold text-red-800">Error Message:</p>
<p class="text-sm text-red-700 mt-1"><%= msg.error_message %></p>
</div>
</div>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
</div>
<!-- Pagination -->
<% if @pagy.pages > 1 %>
<div class="border-t border-gray-200 px-6 py-4 bg-gray-50">
<div class="flex items-center justify-between">
<!-- Page info -->
<div class="text-sm text-gray-700">
Showing
<span class="font-semibold"><%= @pagy.from %></span>
to
<span class="font-semibold"><%= @pagy.to %></span>
of
<span class="font-semibold"><%= @pagy.count %></span>
results
</div>
<!-- Pagination controls -->
<nav class="isolate inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination">
<% if @pagy.prev %>
<%= link_to admin_logs_path(params.permit!.merge(page: @pagy.prev)),
class: "relative inline-flex items-center rounded-l-md px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 transition-colors" do %>
<i class="fas fa-chevron-left"></i>
<span class="sr-only">Previous</span>
<% end %>
<% else %>
<span class="relative inline-flex items-center rounded-l-md px-3 py-2 text-sm font-semibold text-gray-400 ring-1 ring-inset ring-gray-300 cursor-not-allowed">
<i class="fas fa-chevron-left"></i>
</span>
<% end %>
<% @pagy.series.each do |item| %>
<% if item.is_a?(Integer) %>
<% if item == @pagy.page %>
<span class="relative z-10 inline-flex items-center bg-blue-600 px-4 py-2 text-sm font-semibold text-white ring-1 ring-inset ring-blue-600"><%= item %></span>
<% else %>
<%= link_to item, admin_logs_path(params.permit!.merge(page: item)),
class: "relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 transition-colors" %>
<% end %>
<% elsif item == :gap %>
<span class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-700 ring-1 ring-inset ring-gray-300">...</span>
<% end %>
<% end %>
<% if @pagy.next %>
<%= link_to admin_logs_path(params.permit!.merge(page: @pagy.next)),
class: "relative inline-flex items-center rounded-r-md px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 transition-colors" do %>
<span class="sr-only">Next</span>
<i class="fas fa-chevron-right"></i>
<% end %>
<% else %>
<span class="relative inline-flex items-center rounded-r-md px-3 py-2 text-sm font-semibold text-gray-400 ring-1 ring-inset ring-gray-300 cursor-not-allowed">
<i class="fas fa-chevron-right"></i>
</span>
<% end %>
</nav>
<!-- Page size selector -->
<div class="text-sm text-gray-700">
<%= form_with url: admin_logs_path, method: :get, local: true, class: "inline-flex items-center gap-2" do |f| %>
<label for="items" class="font-medium">Per page:</label>
<%= select_tag :items,
options_for_select([10, 25, 50, 100], params[:items] || 50),
onchange: "this.form.submit()",
class: "rounded-md border-gray-300 text-sm focus:border-blue-500 focus:ring-blue-500" %>
<% params.except(:items, :authenticity_token).each do |key, value| %>
<%= hidden_field_tag key, value %>
<% end %>
<% end %>
</div>
</div>
</div>
<% end %>
<% else %>
<!-- Empty state -->
<div class="px-6 py-14 text-center">
<i class="fas fa-inbox text-4xl text-gray-300"></i>
<p class="mt-4 text-sm font-medium text-gray-900">No messages found</p>
<p class="mt-2 text-sm text-gray-500">Try adjusting your filters to see more results.</p>
</div>
<% end %>
</div>
</div>
<script>
function toggleErrorRow(id) {
const row = document.getElementById(id);
if (row) {
row.classList.toggle('hidden');
}
}
</script>