completed SMS gateway project
This commit is contained in:
235
app/views/admin/logs/index.html.erb
Normal file
235
app/views/admin/logs/index.html.erb
Normal file
@@ -0,0 +1,235 @@
|
||||
<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-xs truncate"><%= 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-center">
|
||||
<%== pagy_nav(@pagy) %>
|
||||
</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>
|
||||
Reference in New Issue
Block a user