Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions app/views/beacons/_summary.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="card-elevated p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 m-0">Total Beacons</p>
<p class="text-3xl font-bold text-gray-800 m-0 mt-2"><%= @beacons.count %></p>
</div>
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
</div>
</div>
</div>

<div class="card-elevated p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 m-0">Online Now</p>
<p class="text-3xl font-bold text-green-600 m-0 mt-2"><%= @beacons.count { |b| b[:online] } %></p>
</div>
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
</div>
</div>

<div class="card-elevated p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 m-0">Offline</p>
<p class="text-3xl font-bold text-red-600 m-0 mt-2"><%= @beacons.count { |b| !b[:online] } %></p>
</div>
<div class="w-12 h-12 bg-red-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</div>
</div>
</div>
</div>
85 changes: 85 additions & 0 deletions app/views/beacons/_table.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<div class="card-elevated overflow-hidden">
<div class="table-container overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="table-header">
<th class="table-cell text-left whitespace-nowrap">Status</th>
<th class="table-cell text-left">Beacon Name</th>
<th class="table-cell text-left">Current Manifest Version</th>
<th class="table-cell text-left">Latest Manifest Version</th>
<th class="table-cell text-left">Up to Date?</th>
<th class="table-cell text-left">Last Seen at</th>
<th class="table-cell text-left">Last Sync at</th>
</tr>
</thead>
<tbody>
<% if @beacons.any? %>
<% @beacons.each do |beacon| %>
<tr class="border-b border-gray-100 transition-all hover:bg-gray-50">
<td class="table-cell">
<div class="flex items-center gap-2">
<span class="<%= beacon.revoked? ? 'beacon-offline' : 'beacon-online' %>"></span>
<span class="text-xs font-medium <%= beacon.revoked? ? 'text-red-700' : 'text-green-700' %>">
<%= beacon.revoked? ? 'Revoked' : 'Active' %>
</span>
</div>
</td>
<td class="table-cell">
<%= link_to beacon_path(beacon), class: "no-underline" do %>
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-gradient-green rounded-lg flex items-center justify-center text-white font-semibold text-sm">
<svg class="icon-size-md" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
</div>
<div>
<div class="font-semibold text-gray-900 text-sm"><%= beacon.name %></div>
<div class="text-xs text-gray-500"><%= beacon.api_key_prefix %>***</div>
</div>
</div>
<% end %>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
current manifest
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
latest manifest
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
is up to date?
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
last seen at
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
last sync at
</span>
</td>
</tr>
<% end %>
<% else %>
<tr>
<td colspan="6" class="empty-state">
<div class="text-gray-400">
<svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
<p class="text-base font-medium text-gray-500 m-0">No beacons provisioned yet</p>
<p class="text-sm text-gray-400 mt-2 m-0">Get started by provisioning your first beacon.</p>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
133 changes: 3 additions & 130 deletions app/views/beacons/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<div class="flex items-center gap-3">
<div class="text-right mr-4">
<div class="text-sm text-gray-600">
Active: <span class="font-semibold text-green-600"><%= @beacons.count { |b| !b.revoked? } %></span> /
Active: <span class="font-semibold text-green-600"><%= @beacons.count { |b| !b.revoked? } %></span> /
Revoked: <span class="font-semibold text-red-600"><%= @beacons.count { |b| b.revoked? } %></span>
</div>
</div>
Expand All @@ -36,136 +36,9 @@
</div>
</div>

<!-- Beacons Table Card -->
<div class="card-elevated overflow-hidden">
<div class="table-container overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="table-header">
<th class="table-cell text-left">Status</th>
<th class="table-cell text-left">Beacon Name</th>
<th class="table-cell text-left">Current Manifest Version</th>
<th class="table-cell text-left">Latest Manifest Version</th>
<th class="table-cell text-left">Up to Date?</th>
<th class="table-cell text-left">Last Seen at</th>
<th class="table-cell text-left">Last Sync at</th>
</tr>
</thead>
<tbody>
<% if @beacons.any? %>
<% @beacons.each do |beacon| %>
<tr class="border-b border-gray-100 transition-all hover:bg-gray-50">
<td class="table-cell">
<div class="flex items-center gap-2">
<span class="<%= beacon.revoked? ? 'beacon-offline' : 'beacon-online' %>"></span>
<span class="text-xs font-medium <%= beacon.revoked? ? 'text-red-700' : 'text-green-700' %>">
<%= beacon.revoked? ? 'Revoked' : 'Active' %>
</span>
</div>
</td>
<td class="table-cell">
<%= link_to beacon_path(beacon), class: "no-underline" do %>
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-gradient-green rounded-lg flex items-center justify-center text-white font-semibold text-sm">
<svg class="icon-size-md" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
</div>
<div>
<div class="font-semibold text-gray-900 text-sm"><%= beacon.name %></div>
<div class="text-xs text-gray-500"><%= beacon.api_key_prefix %>***</div>
</div>
</div>
<% end %>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
current manifest
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
latest manifest
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
is up to date?
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
last seen at
</span>
</td>
<td class="table-cell">
<span class="text-sm text-gray-600">
last sync at
</span>
</td>
</tr>
<% end %>
<% else %>
<tr>
<td colspan="6" class="empty-state">
<div class="text-gray-400">
<svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
<p class="text-base font-medium text-gray-500 m-0">No beacons provisioned yet</p>
<p class="text-sm text-gray-400 mt-2 m-0">Get started by provisioning your first beacon.</p>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<%= render "summary" %>

<!-- Statistics Card -->
<div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="card-elevated p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 m-0">Total Beacons</p>
<p class="text-3xl font-bold text-gray-800 m-0 mt-2"><%= @beacons.count %></p>
</div>
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
</div>
</div>
</div>
<%= render "table" %>

<div class="card-elevated p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 m-0">Online Now</p>
<p class="text-3xl font-bold text-green-600 m-0 mt-2"><%= @beacons.count { |b| b[:online] } %></p>
</div>
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
</div>
</div>

<div class="card-elevated p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 m-0">Offline</p>
<p class="text-3xl font-bold text-red-600 m-0 mt-2"><%= @beacons.count { |b| !b[:online] } %></p>
</div>
<div class="w-12 h-12 bg-red-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
8 changes: 8 additions & 0 deletions db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
User.destroy_all
Tag.destroy_all

puts "Creating regions..."

[ "Northeast", "Southeast", "Midwest", "Southwest", "West" ].each do |name|
Region.find_or_create_by!(name: name)
end

puts "Regions created!"

puts "Creating languages..."

[
Expand Down
69 changes: 69 additions & 0 deletions spec/views/beacons/index.html.erb_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require "rails_helper"

RSpec.describe "beacons/index", type: :view do
context "when there are no beacons" do
before do
assign(:beacons, [])
end

it "renders an empty state message" do
render
assert_select "td", text: /No beacons provisioned yet/
end

it "renders the provision new beacon link" do
render
assert_select "a", text: /Provision New Beacon/
end
end

context "when there are beacons" do
let(:active_beacon) { create(:beacon) }
let(:revoked_beacon) { create(:beacon, :revoked) }

before do
assign(:beacons, [active_beacon, revoked_beacon])
end

it "renders a row for each beacon" do
render
assert_select "table tbody tr", count: 2
end

it "shows Active status for active beacons" do
render
assert_select "td span", text: "Active"
end

it "shows Revoked status for revoked beacons" do
render
assert_select "td span", text: "Revoked"
end

it "renders each beacon name" do
render
assert_select "td", text: /#{active_beacon.name}/
assert_select "td", text: /#{revoked_beacon.name}/
end

it "shows the active count in the header" do
render
assert_select "span.text-green-600", text: "1"
end

it "shows the revoked count in the header" do
render
assert_select "span.text-red-600", text: "1"
end

it "shows the total beacon count in the summary" do
render
assert_select "p.text-3xl", text: "2"
end

it "renders the Status column header without text wrapping" do
render
assert_select "th.whitespace-nowrap", text: /Status/
end
end
end