magick-feature-flags 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,126 +1,155 @@
1
- <div class="card">
2
- <div class="card-header">
3
- <h2><%= @feature.display_name || @feature.name %></h2>
4
- <div class="btn-group">
5
- <a href="<%= magick_admin_ui.features_path %>" class="btn btn-secondary btn-sm">← Back to List</a>
6
- <a href="<%= magick_admin_ui.edit_feature_path(@feature.name) %>" class="btn btn-primary btn-sm">Edit</a>
7
- </div>
8
- </div>
1
+ <div class="breadcrumb">
2
+ <a href="<%= magick_admin_ui.features_path %>">Features</a>
3
+ <span class="breadcrumb-sep">/</span>
4
+ <span class="breadcrumb-current"><%= @feature.display_name || @feature.name %></span>
5
+ </div>
9
6
 
10
- <div class="feature-details">
11
- <div class="detail-item">
12
- <div class="detail-label">Name</div>
13
- <div class="detail-value"><%= @feature.name %></div>
14
- </div>
15
- <div class="detail-item">
16
- <div class="detail-label">Type</div>
17
- <div class="detail-value">
18
- <span class="badge badge-info"><%= @feature.type.to_s.capitalize %></span>
19
- </div>
20
- </div>
21
- <% if @feature.group.present? %>
22
- <div class="detail-item">
23
- <div class="detail-label">Group</div>
24
- <div class="detail-value">
25
- <span class="badge badge-info"><%= @feature.group %></span>
26
- </div>
27
- </div>
7
+ <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; flex-wrap: wrap; gap: 16px;">
8
+ <div>
9
+ <h1 class="page-title"><%= @feature.display_name || @feature.name %></h1>
10
+ <% if @feature.description %>
11
+ <p class="page-subtitle"><%= @feature.description %></p>
28
12
  <% end %>
29
- <div class="detail-item">
30
- <div class="detail-label">Status</div>
31
- <div class="detail-value">
32
- <% case @feature.status.to_sym %>
33
- <% when :active %>
34
- <span class="badge badge-success">Active</span>
35
- <% when :deprecated %>
36
- <span class="badge badge-warning">Deprecated</span>
37
- <% when :inactive %>
38
- <span class="badge badge-danger">Inactive</span>
39
- <% else %>
40
- <span class="badge"><%= @feature.status.to_s.capitalize %></span>
41
- <% end %>
42
- </div>
13
+ </div>
14
+ <div class="btn-group">
15
+ <% if @feature.type == :boolean %>
16
+ <% if @feature.enabled? %>
17
+ <%= button_to 'Disable', magick_admin_ui.disable_feature_path(@feature.name), method: :put, class: 'btn btn-danger btn-sm' %>
18
+ <% else %>
19
+ <%= button_to 'Enable', magick_admin_ui.enable_feature_path(@feature.name), method: :put, class: 'btn btn-success btn-sm' %>
20
+ <% end %>
21
+ <% end %>
22
+ <a href="<%= magick_admin_ui.edit_feature_path(@feature.name) %>" class="btn btn-secondary btn-sm">Edit</a>
23
+ <a href="<%= magick_admin_ui.stat_path(@feature.name) %>" class="btn btn-ghost btn-sm">Statistics</a>
24
+ </div>
25
+ </div>
26
+
27
+ <div class="detail-row">
28
+ <div class="detail-chip">
29
+ <span class="detail-chip-label">Key</span>
30
+ <span class="detail-chip-value" style="font-family: var(--font-mono); font-size: 13px;"><%= @feature.name %></span>
31
+ </div>
32
+ <div class="detail-chip">
33
+ <span class="detail-chip-label">Type</span>
34
+ <span class="detail-chip-value"><%= @feature.type.to_s.capitalize %></span>
35
+ </div>
36
+ <% if @feature.group.present? %>
37
+ <div class="detail-chip">
38
+ <span class="detail-chip-label">Group</span>
39
+ <span class="detail-chip-value"><%= @feature.group %></span>
43
40
  </div>
44
- <div class="detail-item">
45
- <div class="detail-label">Current Value</div>
46
- <div class="detail-value">
47
- <% if @feature.type == :boolean %>
48
- <% targeting = @feature.instance_variable_get(:@targeting) || {} %>
49
- <% if targeting.any? %>
50
- <span class="badge badge-warning">Partially Enabled</span>
51
- <small style="display: block; color: #999; margin-top: 4px;">Targeting rules are active</small>
52
- <% elsif @feature.enabled? %>
53
- <span class="badge badge-success">Enabled</span>
54
- <% else %>
55
- <span class="badge badge-danger">Disabled</span>
56
- <% end %>
41
+ <% end %>
42
+ <div class="detail-chip">
43
+ <span class="detail-chip-label">Status</span>
44
+ <span class="detail-chip-value">
45
+ <% case @feature.status.to_sym %>
46
+ <% when :active %>
47
+ <span class="status-indicator"><span class="status-dot status-dot-green"></span> Active</span>
48
+ <% when :deprecated %>
49
+ <span class="status-indicator"><span class="status-dot status-dot-yellow"></span> Deprecated</span>
50
+ <% when :inactive %>
51
+ <span class="status-indicator"><span class="status-dot status-dot-red"></span> Inactive</span>
52
+ <% else %>
53
+ <span class="status-indicator"><%= @feature.status.to_s.capitalize %></span>
54
+ <% end %>
55
+ </span>
56
+ </div>
57
+ <div class="detail-chip">
58
+ <span class="detail-chip-label">Value</span>
59
+ <span class="detail-chip-value">
60
+ <% if @feature.type == :boolean %>
61
+ <% targeting = @feature.instance_variable_get(:@targeting) || {} %>
62
+ <% if targeting.any? %>
63
+ <span class="toggle-indicator toggle-partial">PARTIAL</span>
64
+ <% elsif @feature.enabled? %>
65
+ <span class="toggle-indicator toggle-on">ON</span>
57
66
  <% else %>
58
- <code><%= @feature.get_value.inspect %></code>
67
+ <span class="toggle-indicator toggle-off">OFF</span>
59
68
  <% end %>
60
- </div>
61
- </div>
62
- <div class="detail-item">
63
- <div class="detail-label">Default Value</div>
64
- <div class="detail-value"><code><%= @feature.default_value.inspect %></code></div>
65
- </div>
69
+ <% else %>
70
+ <code><%= @feature.get_value.inspect %></code>
71
+ <% end %>
72
+ </span>
66
73
  </div>
74
+ <div class="detail-chip">
75
+ <span class="detail-chip-label">Default</span>
76
+ <span class="detail-chip-value"><code><%= @feature.default_value.inspect %></code></span>
77
+ </div>
78
+ </div>
67
79
 
68
- <% if @feature.description %>
69
- <div class="card" style="margin-top: 20px;">
70
- <h3 style="margin-bottom: 10px;">Description</h3>
71
- <p><%= @feature.description %></p>
72
- </div>
73
- <% end %>
80
+ <% targeting = @feature.instance_variable_get(:@targeting) || {} %>
81
+ <% inclusion_rules = targeting.reject { |k, _| k.to_s.start_with?('excluded_') } %>
82
+ <% exclusion_rules = targeting.select { |k, _| k.to_s.start_with?('excluded_') } %>
74
83
 
75
- <% targeting = @feature.instance_variable_get(:@targeting) || {} %>
76
- <div class="card targeting-rules" style="margin-top: 20px;">
77
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap; gap: 12px;">
78
- <h3 style="margin: 0;">Targeting Rules</h3>
79
- <a href="<%= magick_admin_ui.edit_feature_path(@feature.name) %>" class="btn btn-secondary btn-sm">Manage Targeting</a>
80
- </div>
81
- <% if targeting.any? %>
82
- <ul>
83
- <% targeting.each do |key, value| %>
84
- <li>
85
- <strong><%= key.to_s.humanize.gsub('Percentage ', '') %>:</strong>
86
- <% if key.to_s == 'percentage_users' || key.to_s == 'percentage_requests' %>
87
- <span class="badge badge-info" style="margin-left: 4px;"><%= value.to_f %>%</span>
88
- <% elsif value.is_a?(Array) %>
89
- <% value.each do |v| %>
90
- <span class="badge badge-info" style="margin-left: 4px;"><%= v %></span>
84
+ <div class="card">
85
+ <div class="card-header">
86
+ <h2>Targeting Rules</h2>
87
+ <a href="<%= magick_admin_ui.edit_feature_path(@feature.name) %>" class="link-subtle">Manage</a>
88
+ </div>
89
+ <div class="card-body">
90
+ <% if inclusion_rules.any? %>
91
+ <div class="targeting-section targeting-section-inclusion">
92
+ <h4>Inclusions</h4>
93
+ <% inclusion_rules.each do |key, value| %>
94
+ <div class="targeting-rule">
95
+ <span class="targeting-rule-label"><%= key.to_s.humanize.gsub('Percentage ', '') %></span>
96
+ <div class="targeting-rule-values">
97
+ <% if key.to_s == 'percentage_users' || key.to_s == 'percentage_requests' %>
98
+ <span class="pill pill-inclusion"><%= value.to_f %>%</span>
99
+ <% elsif value.is_a?(Array) %>
100
+ <% value.each do |v| %>
101
+ <span class="pill pill-inclusion"><%= v %></span>
102
+ <% end %>
103
+ <% else %>
104
+ <span class="pill pill-inclusion"><%= value %></span>
91
105
  <% end %>
92
- <% else %>
93
- <span class="badge badge-info" style="margin-left: 4px;"><%= value %></span>
94
- <% end %>
95
- </li>
106
+ </div>
107
+ </div>
96
108
  <% end %>
97
- </ul>
98
- <% else %>
99
- <p style="color: #999; font-style: italic;">No targeting rules set. Feature is enabled/disabled globally.</p>
109
+ </div>
100
110
  <% end %>
101
- </div>
102
111
 
103
- <% dependencies = @feature.dependencies %>
104
- <% if dependencies.any? %>
105
- <div class="card" style="margin-top: 20px;">
106
- <h3 style="margin-bottom: 10px;">Dependencies</h3>
107
- <ul>
108
- <% dependencies.each do |dep| %>
109
- <li><code><%= dep %></code></li>
112
+ <% if exclusion_rules.any? %>
113
+ <% if inclusion_rules.any? %>
114
+ <div style="margin-top: 20px;"></div>
115
+ <% end %>
116
+ <div class="targeting-section targeting-section-exclusion">
117
+ <h4>Exclusions</h4>
118
+ <% exclusion_rules.each do |key, value| %>
119
+ <div class="targeting-rule targeting-rule-exclusion">
120
+ <span class="targeting-rule-label"><%= key.to_s.sub('excluded_', '').humanize %></span>
121
+ <div class="targeting-rule-values">
122
+ <% if value.is_a?(Array) %>
123
+ <% value.each do |v| %>
124
+ <span class="pill pill-exclusion"><%= v %></span>
125
+ <% end %>
126
+ <% else %>
127
+ <span class="pill pill-exclusion"><%= value %></span>
128
+ <% end %>
129
+ </div>
130
+ </div>
110
131
  <% end %>
111
- </ul>
112
- </div>
113
- <% end %>
132
+ </div>
133
+ <% end %>
114
134
 
115
- <div class="btn-group" style="margin-top: 20px;">
116
- <% if @feature.type == :boolean %>
117
- <% if @feature.enabled? %>
118
- <%= button_to 'Disable', magick_admin_ui.disable_feature_path(@feature.name), method: :put, class: 'btn btn-danger btn-sm' %>
119
- <% else %>
120
- <%= button_to 'Enable', magick_admin_ui.enable_feature_path(@feature.name), method: :put, class: 'btn btn-success btn-sm' %>
121
- <% end %>
135
+ <% if inclusion_rules.empty? && exclusion_rules.empty? %>
136
+ <p style="color: var(--color-text-muted); font-size: 13px;">No targeting rules configured. Feature applies globally.</p>
122
137
  <% end %>
123
- <a href="<%= magick_admin_ui.edit_feature_path(@feature.name) %>" class="btn btn-primary btn-sm">Edit</a>
124
- <a href="<%= magick_admin_ui.stat_path(@feature.name) %>" class="btn btn-secondary btn-sm">View Statistics</a>
125
138
  </div>
126
139
  </div>
140
+
141
+ <% dependencies = @feature.dependencies %>
142
+ <% if dependencies.any? %>
143
+ <div class="card">
144
+ <div class="card-header">
145
+ <h2>Dependencies</h2>
146
+ </div>
147
+ <div class="card-body">
148
+ <div style="display: flex; flex-wrap: wrap; gap: 6px;">
149
+ <% dependencies.each do |dep| %>
150
+ <code><%= dep %></code>
151
+ <% end %>
152
+ </div>
153
+ </div>
154
+ </div>
155
+ <% end %>
@@ -1,67 +1,77 @@
1
- <div class="card">
2
- <div class="card-header">
3
- <h2>Statistics: <%= @feature.display_name || @feature.name %></h2>
4
- <div class="btn-group">
5
- <a href="<%= magick_admin_ui.feature_path(@feature.name) %>" class="btn btn-secondary btn-sm">← Back to Feature</a>
1
+ <div class="breadcrumb">
2
+ <a href="<%= magick_admin_ui.features_path %>">Features</a>
3
+ <span class="breadcrumb-sep">/</span>
4
+ <a href="<%= magick_admin_ui.feature_path(@feature.name) %>"><%= @feature.display_name || @feature.name %></a>
5
+ <span class="breadcrumb-sep">/</span>
6
+ <span class="breadcrumb-current">Statistics</span>
7
+ </div>
8
+
9
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 12px;">
10
+ <h1 class="page-title">Statistics</h1>
11
+ <a href="<%= magick_admin_ui.feature_path(@feature.name) %>" class="btn btn-secondary btn-sm">Back to Feature</a>
12
+ </div>
13
+
14
+ <% if @stats && @stats.any? %>
15
+ <div class="stats-grid" style="margin-bottom: 24px;">
16
+ <div class="stat-card">
17
+ <div class="stat-value"><%= @stats[:usage_count] || 0 %></div>
18
+ <div class="stat-label">Total Usage</div>
19
+ </div>
20
+ <div class="stat-card">
21
+ <div class="stat-value"><%= sprintf('%.3f', @stats[:average_duration] || 0) %><span style="font-size: 14px; font-weight: 500; color: var(--color-text-muted);">ms</span></div>
22
+ <div class="stat-label">Avg Duration</div>
6
23
  </div>
24
+ <% if @stats[:average_duration_by_operation] %>
25
+ <% @stats[:average_duration_by_operation].each do |operation, duration| %>
26
+ <div class="stat-card">
27
+ <div class="stat-value"><%= sprintf('%.3f', duration || 0) %><span style="font-size: 14px; font-weight: 500; color: var(--color-text-muted);">ms</span></div>
28
+ <div class="stat-label"><%= operation.to_s.humanize %></div>
29
+ </div>
30
+ <% end %>
31
+ <% end %>
7
32
  </div>
8
33
 
9
- <% if @stats && @stats.any? %>
10
- <div class="stats-grid">
11
- <div class="stat-card">
12
- <div class="stat-value"><%= @stats[:usage_count] || 0 %></div>
13
- <div class="stat-label">Total Usage</div>
14
- </div>
15
- <div class="stat-card">
16
- <div class="stat-value"><%= sprintf('%.3f', @stats[:average_duration] || 0) %>ms</div>
17
- <div class="stat-label">Avg Duration</div>
18
- </div>
19
- <% if @stats[:average_duration_by_operation] %>
20
- <% @stats[:average_duration_by_operation].each do |operation, duration| %>
21
- <div class="stat-card">
22
- <div class="stat-value"><%= sprintf('%.3f', duration || 0) %>ms</div>
23
- <div class="stat-label"><%= operation.to_s.humanize %></div>
24
- </div>
25
- <% end %>
26
- <% end %>
34
+ <div class="card">
35
+ <div class="card-header">
36
+ <h2>Performance Metrics</h2>
27
37
  </div>
28
-
29
- <div class="card" style="margin-top: 20px;">
30
- <h3 style="margin-bottom: 16px;">Performance Metrics</h3>
38
+ <div class="card-body" style="padding: 0;">
31
39
  <table>
32
40
  <thead>
33
41
  <tr>
34
42
  <th>Metric</th>
35
- <th>Value</th>
43
+ <th style="text-align: right;">Value</th>
36
44
  </tr>
37
45
  </thead>
38
46
  <tbody>
39
47
  <tr>
40
- <td>Usage Count</td>
41
- <td><strong><%= @stats[:usage_count] || 0 %></strong></td>
48
+ <td style="color: var(--color-text-secondary);">Usage Count</td>
49
+ <td style="text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;"><%= @stats[:usage_count] || 0 %></td>
42
50
  </tr>
43
51
  <tr>
44
- <td>Average Duration</td>
45
- <td><strong><%= sprintf('%.3f', @stats[:average_duration] || 0) %>ms</strong></td>
52
+ <td style="color: var(--color-text-secondary);">Average Duration</td>
53
+ <td style="text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;"><%= sprintf('%.3f', @stats[:average_duration] || 0) %> ms</td>
46
54
  </tr>
47
55
  <% if @stats[:average_duration_by_operation] %>
48
56
  <% @stats[:average_duration_by_operation].each do |operation, duration| %>
49
57
  <tr>
50
- <td>Average Duration (<%= operation.to_s.humanize %>)</td>
51
- <td><strong><%= sprintf('%.3f', duration || 0) %>ms</strong></td>
58
+ <td style="color: var(--color-text-secondary);">Avg Duration (<%= operation.to_s.humanize %>)</td>
59
+ <td style="text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;"><%= sprintf('%.3f', duration || 0) %> ms</td>
52
60
  </tr>
53
61
  <% end %>
54
62
  <% end %>
55
63
  </tbody>
56
64
  </table>
57
65
  </div>
58
- <% else %>
66
+ </div>
67
+ <% else %>
68
+ <div class="card">
59
69
  <div class="empty-state">
60
70
  <h3>No statistics available</h3>
61
71
  <p>Performance metrics are not enabled or no data has been collected yet.</p>
62
- <p style="margin-top: 10px; font-size: 14px; color: #999;">
63
- Enable performance metrics in your Magick configuration to start collecting statistics.
72
+ <p style="margin-top: 8px; font-size: 12px; color: var(--color-text-muted);">
73
+ Enable performance metrics in your Magick configuration to start collecting data.
64
74
  </p>
65
75
  </div>
66
- <% end %>
67
- </div>
76
+ </div>
77
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '1.2.0'
4
+ VERSION = '1.2.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magick-feature-flags
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov