|
1 | | -<div class="container mx-auto px-4 py-8"> |
2 | | - <div class="breadcrumbs text-sm mb-4"> |
3 | | - <ul> |
4 | | - <li><%= link_to "Websites", tenant_admin_websites_path %></li> |
5 | | - <li><%= link_to @website.subdomain, tenant_admin_website_path(@website) %></li> |
6 | | - <li>Shard Assignment</li> |
7 | | - </ul> |
8 | | - </div> |
| 1 | +<div class="max-w-4xl mx-auto"> |
| 2 | + <!-- Breadcrumbs --> |
| 3 | + <nav class="text-sm mb-4"> |
| 4 | + <ol class="flex items-center space-x-2 text-gray-500"> |
| 5 | + <li><%= link_to "Websites", tenant_admin_websites_path, class: "hover:text-blue-600" %></li> |
| 6 | + <li><span class="mx-2">/</span></li> |
| 7 | + <li><%= link_to @website.subdomain, tenant_admin_website_path(@website), class: "hover:text-blue-600" %></li> |
| 8 | + <li><span class="mx-2">/</span></li> |
| 9 | + <li class="text-gray-900 font-medium">Shard Assignment</li> |
| 10 | + </ol> |
| 11 | + </nav> |
9 | 12 |
|
10 | | - <h1 class="text-3xl font-bold mb-6">Shard Assignment: <%= @website.subdomain %></h1> |
| 13 | + <!-- Page Header --> |
| 14 | + <div class="mb-6"> |
| 15 | + <h1 class="text-2xl md:text-3xl font-bold text-gray-900">Shard Assignment</h1> |
| 16 | + <p class="mt-1 text-sm text-gray-600"><%= @website.subdomain %></p> |
| 17 | + </div> |
11 | 18 |
|
12 | | - <%# Current Shard Info %> |
13 | | - <div class="alert alert-info mb-6"> |
14 | | - <div> |
15 | | - <div class="flex items-center gap-2"> |
16 | | - <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
17 | | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> |
18 | | - </svg> |
19 | | - <div> |
20 | | - <p><strong>Current shard:</strong> <%= @current_shard %></p> |
21 | | - <p class="text-sm"> |
22 | | - Last changed: |
23 | | - <% if @audit_logs.any? %> |
24 | | - <%= time_ago_in_words(@audit_logs.first.created_at) %> ago |
25 | | - by <%= @audit_logs.first.changed_by_email %> |
26 | | - <% else %> |
27 | | - Never |
28 | | - <% end %> |
29 | | - </p> |
30 | | - </div> |
| 19 | + <!-- Current Shard Info --> |
| 20 | + <div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6"> |
| 21 | + <div class="flex items-start"> |
| 22 | + <svg class="h-5 w-5 text-blue-400 mt-0.5" fill="currentColor" viewBox="0 0 20 20"> |
| 23 | + <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/> |
| 24 | + </svg> |
| 25 | + <div class="ml-3"> |
| 26 | + <p class="text-sm font-medium text-blue-800">Current shard: <strong><%= @current_shard %></strong></p> |
| 27 | + <p class="text-sm text-blue-700 mt-1"> |
| 28 | + Last changed: |
| 29 | + <% if @audit_logs.any? %> |
| 30 | + <%= time_ago_in_words(@audit_logs.first.created_at) %> ago |
| 31 | + by <%= @audit_logs.first.changed_by_email %> |
| 32 | + <% else %> |
| 33 | + Never |
| 34 | + <% end %> |
| 35 | + </p> |
31 | 36 | </div> |
32 | 37 | </div> |
33 | 38 | </div> |
34 | 39 |
|
35 | | - <%# Assignment Form %> |
36 | | - <div class="card bg-base-100 shadow-xl mb-6"> |
37 | | - <div class="card-body"> |
38 | | - <h2 class="card-title">Assign to Different Shard</h2> |
39 | | - |
| 40 | + <!-- Assignment Form --> |
| 41 | + <div class="bg-white rounded-lg shadow mb-6"> |
| 42 | + <div class="px-4 md:px-6 py-4 border-b border-gray-200"> |
| 43 | + <h2 class="text-lg font-semibold text-gray-900">Assign to Different Shard</h2> |
| 44 | + </div> |
| 45 | + <div class="p-4 md:p-6"> |
40 | 46 | <%= form_with url: assign_shard_tenant_admin_website_path(@website), |
41 | 47 | method: :patch, |
42 | 48 | local: true, |
43 | | - class: "space-y-4 mt-4", |
| 49 | + class: "space-y-4", |
44 | 50 | data: { turbo: true } do |f| %> |
45 | | - |
46 | | - <div class="form-control"> |
47 | | - <%= label_tag :shard_name, "Target Shard", class: "label" %> |
48 | | - <%= select_tag :shard_name, |
| 51 | + |
| 52 | + <div> |
| 53 | + <label for="shard_name" class="block text-sm font-medium text-gray-700 mb-1">Target Shard</label> |
| 54 | + <%= select_tag :shard_name, |
49 | 55 | options_for_select(@available_shards, @current_shard), |
50 | | - class: "select select-bordered w-full", |
| 56 | + class: "block w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm", |
51 | 57 | required: true %> |
52 | | - <label class="label"> |
53 | | - <span class="label-text-alt"> |
54 | | - Choose the database shard for this website |
55 | | - </span> |
56 | | - </label> |
| 58 | + <p class="mt-1 text-sm text-gray-500">Choose the database shard for this website</p> |
57 | 59 | </div> |
58 | 60 |
|
59 | | - <div class="form-control"> |
60 | | - <%= label_tag :notes, "Reason for Change", class: "label" %> |
61 | | - <%= text_area_tag :notes, |
| 61 | + <div> |
| 62 | + <label for="notes" class="block text-sm font-medium text-gray-700 mb-1">Reason for Change</label> |
| 63 | + <%= text_area_tag :notes, |
62 | 64 | nil, |
63 | 65 | rows: 3, |
64 | 66 | placeholder: "Optional: Explain why you're changing shards", |
65 | | - class: "textarea textarea-bordered w-full" %> |
| 67 | + class: "block w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" %> |
66 | 68 | </div> |
67 | 69 |
|
68 | | - <div class="alert alert-warning"> |
69 | | - <div> |
70 | | - <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
71 | | - <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /> |
| 70 | + <!-- Warning --> |
| 71 | + <div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4"> |
| 72 | + <div class="flex"> |
| 73 | + <svg class="h-5 w-5 text-yellow-400" fill="currentColor" viewBox="0 0 20 20"> |
| 74 | + <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/> |
72 | 75 | </svg> |
73 | | - <div> |
74 | | - <p class="font-bold">Warning: Routing Change Only</p> |
75 | | - <p class="text-sm"> |
| 76 | + <div class="ml-3"> |
| 77 | + <h3 class="text-sm font-medium text-yellow-800">Warning: Routing Change Only</h3> |
| 78 | + <p class="text-sm text-yellow-700 mt-1"> |
76 | 79 | This only updates the shard routing configuration. Data is NOT automatically migrated. |
77 | 80 | To migrate data, you must use the migration wizard separately (Phase 3 feature). |
78 | 81 | </p> |
79 | 82 | </div> |
80 | 83 | </div> |
81 | 84 | </div> |
82 | 85 |
|
83 | | - <div class="card-actions justify-end"> |
84 | | - <%= link_to "Cancel", |
85 | | - tenant_admin_website_path(@website), |
86 | | - class: "btn btn-ghost" %> |
87 | | - <%= f.submit "Assign to Shard", |
88 | | - class: "btn btn-primary", |
| 86 | + <div class="flex justify-end gap-3 pt-4"> |
| 87 | + <%= link_to "Cancel", |
| 88 | + tenant_admin_website_path(@website), |
| 89 | + class: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50" %> |
| 90 | + <%= f.submit "Assign to Shard", |
| 91 | + class: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-lg hover:bg-blue-700 cursor-pointer", |
89 | 92 | data: { confirm: "Are you sure? This will change where database queries are routed for this website." } %> |
90 | 93 | </div> |
91 | 94 | <% end %> |
92 | 95 | </div> |
93 | 96 | </div> |
94 | 97 |
|
95 | | - <%# Current Shard Health %> |
| 98 | + <!-- Current Shard Health --> |
96 | 99 | <% if @shard_health %> |
97 | | - <div class="card bg-base-100 shadow-xl mb-6"> |
98 | | - <div class="card-body"> |
99 | | - <h2 class="card-title">Current Shard Health</h2> |
100 | | - |
101 | | - <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4"> |
102 | | - <div class="stat bg-base-200 rounded-lg p-4"> |
103 | | - <div class="stat-title text-xs">Status</div> |
104 | | - <div class="stat-value text-lg"> |
105 | | - <span class="badge badge-<%= @shard_health.status_color %> badge-lg"> |
| 100 | + <div class="bg-white rounded-lg shadow mb-6"> |
| 101 | + <div class="px-4 md:px-6 py-4 border-b border-gray-200"> |
| 102 | + <h2 class="text-lg font-semibold text-gray-900">Current Shard Health</h2> |
| 103 | + </div> |
| 104 | + <div class="p-4 md:p-6"> |
| 105 | + <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> |
| 106 | + <div class="bg-gray-50 rounded-lg p-4"> |
| 107 | + <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Status</p> |
| 108 | + <p class="mt-2"> |
| 109 | + <% status_class = case @shard_health.status_color |
| 110 | + when 'success' then 'bg-green-100 text-green-800' |
| 111 | + when 'error' then 'bg-red-100 text-red-800' |
| 112 | + when 'warning' then 'bg-yellow-100 text-yellow-800' |
| 113 | + else 'bg-gray-100 text-gray-800' |
| 114 | + end %> |
| 115 | + <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium <%= status_class %>"> |
106 | 116 | <%= @shard_health.status_label %> |
107 | 117 | </span> |
108 | | - </div> |
| 118 | + </p> |
109 | 119 | </div> |
110 | | - |
111 | | - <div class="stat bg-base-200 rounded-lg p-4"> |
112 | | - <div class="stat-title text-xs">Avg Query Time</div> |
113 | | - <div class="stat-value text-lg"> |
| 120 | + |
| 121 | + <div class="bg-gray-50 rounded-lg p-4"> |
| 122 | + <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Avg Query Time</p> |
| 123 | + <p class="mt-2 text-xl font-semibold text-gray-900"> |
114 | 124 | <%= @shard_health.avg_query_ms || 'N/A' %><% if @shard_health.avg_query_ms %>ms<% end %> |
115 | | - </div> |
| 125 | + </p> |
116 | 126 | </div> |
117 | | - |
118 | | - <div class="stat bg-base-200 rounded-lg p-4"> |
119 | | - <div class="stat-title text-xs">Database Size</div> |
120 | | - <div class="stat-value text-sm"> |
| 127 | + |
| 128 | + <div class="bg-gray-50 rounded-lg p-4"> |
| 129 | + <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Database Size</p> |
| 130 | + <p class="mt-2 text-xl font-semibold text-gray-900"> |
121 | 131 | <%= number_to_human_size(@shard_health.database_size) if @shard_health.database_size %> |
122 | | - </div> |
| 132 | + </p> |
123 | 133 | </div> |
124 | 134 | </div> |
125 | 135 | </div> |
126 | 136 | </div> |
127 | 137 | <% end %> |
128 | 138 |
|
129 | | - <%# Shard Assignment History %> |
130 | | - <div class="card bg-base-100 shadow-xl"> |
131 | | - <div class="card-body"> |
132 | | - <div class="flex justify-between items-center mb-4"> |
133 | | - <h2 class="card-title">Shard Assignment History</h2> |
134 | | - <%= link_to "View All", |
135 | | - shard_history_tenant_admin_website_path(@website), |
136 | | - class: "btn btn-sm btn-ghost" if @audit_logs.count >= 5 %> |
137 | | - </div> |
138 | | - |
| 139 | + <!-- Shard Assignment History --> |
| 140 | + <div class="bg-white rounded-lg shadow"> |
| 141 | + <div class="px-4 md:px-6 py-4 border-b border-gray-200 flex justify-between items-center"> |
| 142 | + <h2 class="text-lg font-semibold text-gray-900">Shard Assignment History</h2> |
| 143 | + <% if @audit_logs.count >= 5 %> |
| 144 | + <%= link_to "View All", |
| 145 | + shard_history_tenant_admin_website_path(@website), |
| 146 | + class: "text-sm text-blue-600 hover:text-blue-800" %> |
| 147 | + <% end %> |
| 148 | + </div> |
| 149 | + <div class="p-4 md:p-6"> |
139 | 150 | <% if @audit_logs.any? %> |
140 | 151 | <div class="overflow-x-auto"> |
141 | | - <table class="table table-zebra w-full"> |
| 152 | + <table class="min-w-full divide-y divide-gray-200"> |
142 | 153 | <thead> |
143 | 154 | <tr> |
144 | | - <th>Date</th> |
145 | | - <th>From</th> |
146 | | - <th>To</th> |
147 | | - <th>Changed By</th> |
148 | | - <th>Notes</th> |
149 | | - <th>Status</th> |
| 155 | + <th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> |
| 156 | + <th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">From</th> |
| 157 | + <th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">To</th> |
| 158 | + <th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Changed By</th> |
| 159 | + <th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Notes</th> |
| 160 | + <th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th> |
150 | 161 | </tr> |
151 | 162 | </thead> |
152 | | - <tbody> |
| 163 | + <tbody class="bg-white divide-y divide-gray-200"> |
153 | 164 | <% @audit_logs.each do |log| %> |
154 | 165 | <tr> |
155 | | - <td class="text-sm"><%= log.created_at.to_fs(:short) %></td> |
156 | | - <td><%= log.old_shard_name || 'N/A' %></td> |
157 | | - <td class="font-medium"><%= log.new_shard_name %></td> |
158 | | - <td class="text-sm"><%= log.changed_by_email %></td> |
159 | | - <td class="text-sm"><%= truncate(log.notes, length: 50) if log.notes %></td> |
160 | | - <td> |
| 166 | + <td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500"><%= log.created_at.to_fs(:short) %></td> |
| 167 | + <td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500"><%= log.old_shard_name || 'N/A' %></td> |
| 168 | + <td class="px-3 py-4 whitespace-nowrap text-sm font-medium text-gray-900"><%= log.new_shard_name %></td> |
| 169 | + <td class="px-3 py-4 whitespace-nowrap text-sm text-gray-500"><%= log.changed_by_email %></td> |
| 170 | + <td class="px-3 py-4 text-sm text-gray-500"><%= truncate(log.notes, length: 50) if log.notes %></td> |
| 171 | + <td class="px-3 py-4 whitespace-nowrap"> |
161 | 172 | <% if log.successful? %> |
162 | | - <span class="badge badge-success badge-sm">✓</span> |
| 173 | + <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800"> |
| 174 | + <svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20"> |
| 175 | + <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/> |
| 176 | + </svg> |
| 177 | + Success |
| 178 | + </span> |
163 | 179 | <% elsif log.failed? %> |
164 | | - <span class="badge badge-error badge-sm">✗</span> |
| 180 | + <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800"> |
| 181 | + <svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20"> |
| 182 | + <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/> |
| 183 | + </svg> |
| 184 | + Failed |
| 185 | + </span> |
165 | 186 | <% else %> |
166 | | - <span class="badge badge-warning badge-sm">⏳</span> |
| 187 | + <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800"> |
| 188 | + <svg class="w-3 h-3 mr-1 animate-spin" fill="none" viewBox="0 0 24 24"> |
| 189 | + <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> |
| 190 | + <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> |
| 191 | + </svg> |
| 192 | + Pending |
| 193 | + </span> |
167 | 194 | <% end %> |
168 | 195 | </td> |
169 | 196 | </tr> |
|
0 commit comments