Files
baffle-hub/app/views/data_imports/index.html.erb

273 lines
13 KiB
Plaintext

<%# Helper methods %>
<% def status_badge_class(status) %>
<% case status %>
<% when 'pending' %>
bg-gray-100 text-gray-800
<% when 'processing' %>
bg-blue-100 text-blue-800
<% when 'completed' %>
bg-green-100 text-green-800
<% when 'failed' %>
bg-red-100 text-red-800
<% else %>
bg-gray-100 text-gray-800
<% end %>
<% end %>
<div class="max-w-7xl mx-auto">
<!-- Header -->
<div class="bg-white shadow-sm rounded-lg mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-semibold text-gray-900">GeoLite2 Data Imports</h1>
<p class="mt-1 text-sm text-gray-600">
Manage and monitor your GeoLite2 database imports.
</p>
</div>
<%= link_to "New Import", new_data_import_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
</div>
</div>
</div>
<!-- Filters -->
<div class="bg-white shadow-sm rounded-lg mb-6">
<div class="px-6 py-4">
<%= form_with(url: data_imports_path, method: :get, local: true) do |f| %>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<%= f.label :import_type, "Import Type", class: "block text-sm font-medium text-gray-700 mb-1" %>
<%= f.select :import_type,
options_for_select([['All Types', ''], ['ASN', 'asn'], ['Country', 'country']], params[:import_type]),
{ }, { class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" } %>
</div>
<div>
<%= f.label :status, "Status", class: "block text-sm font-medium text-gray-700 mb-1" %>
<%= f.select :status,
options_for_select([['All Statuses', ''], ['Pending', 'pending'], ['Processing', 'processing'], ['Completed', 'completed'], ['Failed', 'failed']], params[:status]),
{ }, { class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" } %>
</div>
<div>
<%= f.label :filename, "Filename", class: "block text-sm font-medium text-gray-700 mb-1" %>
<%= f.text_field :filename, value: params[:filename], class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm", placeholder: "Search filename..." %>
</div>
<div class="flex items-end">
<%= f.submit "Filter", class: "w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
</div>
</div>
<% end %>
</div>
</div>
<!-- Stats Overview -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div class="bg-white shadow-sm rounded-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
</svg>
</div>
</div>
<div class="ml-4">
<div class="text-2xl font-semibold text-gray-900"><%= DataImport.count %></div>
<div class="text-sm text-gray-600">Total Imports</div>
</div>
</div>
</div>
<div class="bg-white shadow-sm rounded-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<div class="text-2xl font-semibold text-gray-900"><%= DataImport.completed.count %></div>
<div class="text-sm text-gray-600">Completed</div>
</div>
</div>
</div>
<div class="bg-white shadow-sm rounded-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
</svg>
</div>
</div>
<div class="ml-4">
<div class="text-2xl font-semibold text-gray-900"><%= DataImport.processing.count %></div>
<div class="text-sm text-gray-600">Processing</div>
</div>
</div>
</div>
<div class="bg-white shadow-sm rounded-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<div class="text-2xl font-semibold text-gray-900"><%= DataImport.failed.count %></div>
<div class="text-sm text-gray-600">Failed</div>
</div>
</div>
</div>
</div>
<!-- Imports Table -->
<div class="bg-white shadow-sm rounded-lg">
<div class="overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Filename
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Type
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Progress
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Created
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Duration
</th>
<th class="relative px-6 py-3">
<span class="sr-only">Actions</span>
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<% if @data_imports.any? %>
<% @data_imports.each do |data_import| %>
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<%= link_to data_import, class: "flex items-center text-blue-600 hover:text-blue-900 hover:underline" do %>
<svg class="w-4 h-4 text-gray-400 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<%= truncate(data_import.filename, length: 40) %>
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%= data_import.import_type == 'asn' ? 'bg-purple-100 text-purple-800' : 'bg-indigo-100 text-indigo-800' %>">
<%= data_import.import_type.upcase %>
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%= status_badge_class(data_import.status) %>">
<%= data_import.status.capitalize %>
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<%= link_to data_import, class: "block hover:bg-gray-50 -mx-2 px-2 py-1 rounded" do %>
<% if data_import.processing? || data_import.total_records > 0 %>
<div class="flex items-center">
<div class="flex-1 mr-2">
<div class="w-full bg-gray-200 rounded-full h-2">
<div class="bg-blue-600 h-2 rounded-full" style="width: <%= data_import.progress_percentage %>%"></div>
</div>
</div>
<span class="text-xs text-gray-600">
<% if data_import.processed_records > 0 %>
<% if data_import.total_records > 0 && data_import.processed_records >= data_import.total_records %>
<%= number_with_delimiter(data_import.processed_records) %> total
<% else %>
<%= number_with_delimiter(data_import.processed_records) %> imported
<% end %>
<% else %>
Initializing...
<% end %>
</span>
</div>
<% else %>
<span class="text-gray-400">Not started</span>
<% end %>
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<%= data_import.created_at.strftime('%Y-%m-%d %H:%M') %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<% if data_import.duration > 0 %>
<%= distance_of_time_in_words(data_import.duration) %>
<% else %>
-
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<% unless data_import.processing? %>
<%= link_to "Delete", data_import, method: :delete,
data: {
confirm: "Are you sure you want to delete this import?"
},
class: "text-red-600 hover:text-red-900" %>
<% else %>
<span class="text-gray-400">Processing...</span>
<% end %>
</td>
</tr>
<% end %>
<% else %>
<tr>
<td colspan="7" class="px-6 py-12 text-center">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No imports found</h3>
<p class="mt-1 text-sm text-gray-500">
<% if params[:import_type].present? || params[:status].present? || params[:filename].present? %>
Try adjusting your search filters or
<% else %>
Get started by uploading your first
<% end %>
<%= link_to "GeoLite2 import", new_data_import_path, class: "text-blue-600 hover:text-blue-500" %>.
</p>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<!-- Pagination -->
<% if @pagy.pages > 1 %>
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p class="text-sm text-gray-700">
Showing
<span class="font-medium"><%= @pagy.from %></span>
to
<span class="font-medium"><%= @pagy.to %></span>
of
<span class="font-medium"><%= @pagy.count %></span>
results
</p>
</div>
<div>
<%= pagy_nav_tailwind(@pagy) %>
</div>
</div>
</div>
<% end %>
</div>
</div>