exception_hunter 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +103 -6
- data/app/assets/stylesheets/exception_hunter/base.css +66 -8
- data/app/assets/stylesheets/exception_hunter/errors.css +188 -25
- data/app/assets/stylesheets/exception_hunter/navigation.css +20 -5
- data/app/assets/stylesheets/exception_hunter/sessions.css +71 -0
- data/app/controllers/concerns/exception_hunter/authorization.rb +23 -0
- data/app/controllers/exception_hunter/application_controller.rb +2 -0
- data/app/controllers/exception_hunter/errors_controller.rb +29 -4
- data/app/controllers/exception_hunter/ignored_errors_controller.rb +25 -0
- data/app/controllers/exception_hunter/resolved_errors_controller.rb +11 -0
- data/app/helpers/exception_hunter/application_helper.rb +22 -0
- data/app/helpers/exception_hunter/sessions_helper.rb +16 -0
- data/app/jobs/exception_hunter/send_notification_job.rb +15 -0
- data/app/models/exception_hunter/application_record.rb +8 -0
- data/app/models/exception_hunter/error.rb +24 -7
- data/app/models/exception_hunter/error_group.rb +24 -5
- data/app/presenters/exception_hunter/dashboard_presenter.rb +56 -0
- data/app/presenters/exception_hunter/error_group_presenter.rb +25 -0
- data/app/presenters/exception_hunter/error_presenter.rb +3 -2
- data/app/views/exception_hunter/devise/sessions/new.html.erb +24 -0
- data/app/views/exception_hunter/errors/_error_row.erb +52 -0
- data/app/views/exception_hunter/errors/_error_summary.erb +5 -5
- data/app/views/exception_hunter/errors/_errors_table.erb +1 -0
- data/app/views/exception_hunter/errors/_last_7_days_errors_table.erb +12 -0
- data/app/views/exception_hunter/errors/index.html.erb +84 -29
- data/app/views/exception_hunter/errors/pagy/_pagy_nav.html.erb +15 -15
- data/app/views/exception_hunter/errors/show.html.erb +58 -22
- data/app/views/layouts/exception_hunter/application.html.erb +67 -6
- data/app/views/layouts/exception_hunter/exception_hunter_logged_out.html.erb +24 -0
- data/config/rails_best_practices.yml +3 -3
- data/config/routes.rb +21 -1
- data/lib/exception_hunter.rb +44 -2
- data/lib/exception_hunter/config.rb +25 -1
- data/lib/exception_hunter/data_redacter.rb +27 -0
- data/lib/exception_hunter/devise.rb +19 -0
- data/lib/exception_hunter/engine.rb +6 -0
- data/lib/exception_hunter/error_creator.rb +72 -0
- data/lib/exception_hunter/error_reaper.rb +20 -0
- data/lib/exception_hunter/middleware/delayed_job_hunter.rb +70 -0
- data/lib/exception_hunter/middleware/request_hunter.rb +5 -2
- data/lib/exception_hunter/middleware/sidekiq_hunter.rb +1 -1
- data/lib/exception_hunter/notifiers/misconfigured_notifiers.rb +10 -0
- data/lib/exception_hunter/notifiers/slack_notifier.rb +42 -0
- data/lib/exception_hunter/notifiers/slack_notifier_serializer.rb +20 -0
- data/lib/exception_hunter/tracking.rb +35 -0
- data/lib/exception_hunter/user_attributes_collector.rb +21 -0
- data/lib/exception_hunter/version.rb +1 -1
- data/lib/generators/exception_hunter/create_users/create_users_generator.rb +8 -1
- data/lib/generators/exception_hunter/install/install_generator.rb +3 -1
- data/lib/generators/exception_hunter/install/templates/create_exception_hunter_error_groups.rb.erb +3 -0
- data/lib/generators/exception_hunter/install/templates/exception_hunter.rb.erb +52 -0
- data/lib/tasks/exception_hunter_tasks.rake +6 -4
- metadata +46 -12
- data/app/services/exception_hunter/error_creator.rb +0 -41
- data/config/initializers/exception_hunter.rb +0 -16
@@ -0,0 +1,25 @@
|
|
1
|
+
module ExceptionHunter
|
2
|
+
class ErrorGroupPresenter
|
3
|
+
delegate_missing_to :error_group
|
4
|
+
|
5
|
+
def initialize(error_group)
|
6
|
+
@error_group = error_group
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.wrap_collection(collection)
|
10
|
+
collection.map { |error_group| new(error_group) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.format_occurrence_day(day)
|
14
|
+
day.to_date.strftime('%A, %B %d')
|
15
|
+
end
|
16
|
+
|
17
|
+
def show_for_day?(day)
|
18
|
+
last_occurrence.in_time_zone.to_date == day.to_date
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :error_group
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module ExceptionHunter
|
2
2
|
class ErrorPresenter
|
3
3
|
delegate_missing_to :error
|
4
|
+
delegate :tags, to: :error_group
|
4
5
|
|
5
6
|
BacktraceLine = Struct.new(:path, :file_name, :line_number, :method_call)
|
6
7
|
|
@@ -9,13 +10,13 @@ module ExceptionHunter
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def backtrace
|
12
|
-
error.backtrace.map do |line|
|
13
|
+
(error.backtrace || []).map do |line|
|
13
14
|
format_backtrace_line(line)
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
18
|
def environment_data
|
18
|
-
error.environment_data
|
19
|
+
error.environment_data&.except('params') || {}
|
19
20
|
end
|
20
21
|
|
21
22
|
def tracked_params
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<div class="login_form_container">
|
2
|
+
<div class="login_left_container">
|
3
|
+
<div class="left_column">Exception Hunter</div>
|
4
|
+
</div>
|
5
|
+
<div class="login_right_container">
|
6
|
+
<%= form_for(resource, as: resource_name, url: exception_hunter_create_session_path) do |f| %>
|
7
|
+
<div class="row">
|
8
|
+
Sign into your account
|
9
|
+
</div>
|
10
|
+
<div class="login_row row"></div>
|
11
|
+
<div class="login_row row">
|
12
|
+
<%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: 'Email' %>
|
13
|
+
</div>
|
14
|
+
<div class="login_row row">
|
15
|
+
<%= f.password_field :password, autocomplete: "current-password", placeholder: 'Password'%>
|
16
|
+
</div>
|
17
|
+
<div class="login_button row">
|
18
|
+
<div class="column column-50 column-offset-50">
|
19
|
+
<input class="button-log-in" type="submit" value="Log in">
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
<% end %>
|
23
|
+
</div>
|
24
|
+
</div>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<div class="row error-row">
|
2
|
+
<div class="column column-10 error-cell">
|
3
|
+
<% error.tags.each do |tag| %>
|
4
|
+
<div class="error-cell__tags">
|
5
|
+
<span class="error-tag"><%= tag %></span>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
8
|
+
</div>
|
9
|
+
<div class="column column-33 error-cell error-cell__message">
|
10
|
+
<%= link_to error.message, error_path(error.id), class: %w[error-message] %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="column column-15 error-cell">
|
14
|
+
<% if error.first_occurrence.present? %>
|
15
|
+
<%= time_ago_in_words(error.first_occurrence) %> ago
|
16
|
+
<% else %>
|
17
|
+
Never
|
18
|
+
<% end %>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="column column-15 error-cell">
|
22
|
+
<% if error.last_occurrence.present? %>
|
23
|
+
<%= time_ago_in_words(error.last_occurrence) %> ago
|
24
|
+
<% else %>
|
25
|
+
Never
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<div class="column column-8 error-cell">
|
30
|
+
<%= error.total_occurrences %>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<div class="column column-18 error-cell">
|
34
|
+
<div class="row">
|
35
|
+
<% if error.active? %>
|
36
|
+
<div class="column mr-5">
|
37
|
+
<%= display_action_button('resolve', error) %>
|
38
|
+
</div>
|
39
|
+
<div class="column">
|
40
|
+
<%= display_action_button('ignore', error) %>
|
41
|
+
</div>
|
42
|
+
<% elsif error.ignored? %>
|
43
|
+
<div class="column">
|
44
|
+
<%= button_to('Reopen', reopen_path(error_group: { id: error.id }),
|
45
|
+
class: %w[button button-outline resolve-button],
|
46
|
+
data: { confirm: 'Are you sure you want to reopen this error?' }) %>
|
47
|
+
</div>
|
48
|
+
<% end %>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
|
@@ -1,9 +1,9 @@
|
|
1
1
|
<% if error.environment_data.empty? %>
|
2
|
-
<div class="row error-row
|
2
|
+
<div class="row error-row data-title">
|
3
3
|
Unfortunately, no environment information has been registered for this error.
|
4
4
|
</div>
|
5
5
|
<% else %>
|
6
|
-
<div class="row error-row
|
6
|
+
<div class="row error-row data-title">
|
7
7
|
Environment Data
|
8
8
|
</div>
|
9
9
|
<pre class="tracked-data">
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<% end %>
|
14
14
|
|
15
15
|
<% unless error.tracked_params.nil? %>
|
16
|
-
<div class="row error-row
|
16
|
+
<div class="row error-row data-title">
|
17
17
|
Tracked Params
|
18
18
|
</div>
|
19
19
|
<pre class="tracked-data">
|
@@ -23,11 +23,11 @@
|
|
23
23
|
<% end %>
|
24
24
|
|
25
25
|
<% if error.custom_data.nil? %>
|
26
|
-
<div class="row error-row
|
26
|
+
<div class="row error-row data-title">
|
27
27
|
No custom data included.
|
28
28
|
</div>
|
29
29
|
<% else %>
|
30
|
-
<div class="row error-row
|
30
|
+
<div class="row error-row data-title">
|
31
31
|
Custom Data
|
32
32
|
</div>
|
33
33
|
<pre class="tracked-data">
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render partial: 'exception_hunter/errors/error_row', collection: errors, as: :error %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% today_errors = errors.select { |error| error.show_for_day?(Date.current) } %>
|
2
|
+
<%= render partial: 'exception_hunter/errors/error_row', collection: today_errors, as: :error %>
|
3
|
+
|
4
|
+
<% yesterday_errors = errors.select { |error| error.show_for_day?(Date.yesterday) } %>
|
5
|
+
<div class="errors-date-group">Yesterday</div>
|
6
|
+
<%= render partial: 'exception_hunter/errors/error_row', collection: yesterday_errors, as: :error %>
|
7
|
+
|
8
|
+
<% (2..6).each do |i| %>
|
9
|
+
<% errors_on_day = errors.select { |error| error.show_for_day?(i.days.ago) } %>
|
10
|
+
<div class="errors-date-group"><%= ExceptionHunter::ErrorGroupPresenter.format_occurrence_day(i.days.ago) %></div>
|
11
|
+
<%= render partial: 'exception_hunter/errors/error_row', collection: errors_on_day, as: :error %>
|
12
|
+
<% end %>
|
@@ -1,36 +1,91 @@
|
|
1
|
-
<div class="row
|
2
|
-
<div class="column column-
|
3
|
-
<div class="
|
4
|
-
<%=
|
1
|
+
<div class="row">
|
2
|
+
<div class="column column-80">
|
3
|
+
<div class="errors-tabs">
|
4
|
+
<div class="errors-tab <%= 'errors-tab--active' if @dashboard.tab_active?(@dashboard.class::LAST_7_DAYS_TAB) %>">
|
5
|
+
<%= link_to errors_path(tab: @dashboard.class::LAST_7_DAYS_TAB) do %>
|
6
|
+
<div class="errors-tab__content">
|
7
|
+
<div class="errors-tab__badge">
|
8
|
+
<%= @dashboard.errors_count(@dashboard.class::LAST_7_DAYS_TAB) %>
|
9
|
+
</div>
|
10
|
+
<div class="errors-tab__description">
|
11
|
+
Errors in the last 7 days
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="errors-tab <%= 'errors-tab--active' if @dashboard.tab_active?(@dashboard.class::CURRENT_MONTH_TAB) %>">
|
18
|
+
<%= link_to errors_path(tab: @dashboard.class::CURRENT_MONTH_TAB) do %>
|
19
|
+
<div class="errors-tab__content">
|
20
|
+
<div class="errors-tab__badge">
|
21
|
+
<%= @dashboard.errors_count(@dashboard.class::CURRENT_MONTH_TAB) %>
|
22
|
+
</div>
|
23
|
+
<div class="errors-tab__description">
|
24
|
+
Errors this month
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div class="errors-tab <%= 'errors-tab--active' if @dashboard.tab_active?(@dashboard.class::TOTAL_ERRORS_TAB) %>">
|
31
|
+
<%= link_to errors_path(tab: @dashboard.class::TOTAL_ERRORS_TAB) do %>
|
32
|
+
<div class="errors-tab__content">
|
33
|
+
<div class="errors-tab__badge">
|
34
|
+
<%= @dashboard.errors_count(@dashboard.class::TOTAL_ERRORS_TAB) %>
|
35
|
+
</div>
|
36
|
+
<div class="errors-tab__description">
|
37
|
+
Total errors
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<% end %>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div class="errors-tab errors-tab__resolved <%= 'errors-tab--active' if @dashboard.tab_active?(@dashboard.class::RESOLVED_ERRORS_TAB) %>">
|
44
|
+
<%= link_to errors_path(tab: @dashboard.class::RESOLVED_ERRORS_TAB) do %>
|
45
|
+
<div class="errors-tab__content">
|
46
|
+
<div class="errors-tab__badge">
|
47
|
+
<%= @dashboard.errors_count(@dashboard.class::RESOLVED_ERRORS_TAB) || '-' %>
|
48
|
+
</div>
|
49
|
+
<div class="errors-tab__description">
|
50
|
+
Resolved
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
<div class="errors-tab errors-tab__ignored <%= 'errors-tab--active' if @dashboard.tab_active?(@dashboard.class::IGNORED_ERRORS_TAB) %>">
|
57
|
+
<%= link_to errors_path(tab: @dashboard.class::IGNORED_ERRORS_TAB) do %>
|
58
|
+
<div class="errors-tab__content">
|
59
|
+
<div class="errors-tab__badge">
|
60
|
+
<%= @dashboard.errors_count(@dashboard.class::IGNORED_ERRORS_TAB) || '-' %>
|
61
|
+
</div>
|
62
|
+
<div class="errors-tab__description">
|
63
|
+
Ignored
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
<% end %>
|
67
|
+
</div>
|
5
68
|
</div>
|
6
69
|
</div>
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
70
|
+
|
71
|
+
<div class="column column-10 column-offset-10">
|
72
|
+
<%= button_to 'Purge', purge_errors_path,
|
73
|
+
class: %w[button purge-button],
|
74
|
+
method: :delete,
|
75
|
+
data: { confirm: 'This will delete all stale errors, do you want to continue?' } %>
|
11
76
|
</div>
|
12
77
|
</div>
|
13
78
|
|
14
|
-
<div class="
|
15
|
-
<div class="
|
16
|
-
|
17
|
-
|
18
|
-
</div>
|
79
|
+
<div class="errors__container">
|
80
|
+
<div class="row error-row error-row--header">
|
81
|
+
<div class="column column-10">Tags</div>
|
82
|
+
<div class="column column-33">Message</div>
|
83
|
+
<div class="column column-15">First Occurrence</div>
|
84
|
+
<div class="column column-15">Last Occurrence</div>
|
85
|
+
<div class="column column-8">Total</div>
|
86
|
+
<div class="column column-18"></div>
|
19
87
|
|
20
|
-
<% @errors.each do |error| %>
|
21
|
-
<div class="row error-row">
|
22
|
-
<div class="column column-75 error-cell">
|
23
|
-
<%= link_to error.message, error_path(error.id) %>
|
24
|
-
</div>
|
25
|
-
<div class="column column-15 error-cell error-cell--highlight">
|
26
|
-
<% if error.last_occurrence.present? %>
|
27
|
-
<%= time_ago_in_words(error.last_occurrence) %> ago
|
28
|
-
<% else %>
|
29
|
-
Never
|
30
|
-
<% end %>
|
31
|
-
</div>
|
32
|
-
<div class="column column-10 error-cell error-cell--highlight">
|
33
|
-
<%= error.total_occurrences %>
|
34
|
-
</div>
|
35
88
|
</div>
|
36
|
-
|
89
|
+
|
90
|
+
<%= render partial: @dashboard.partial_for_tab, locals: { errors: @errors } %>
|
91
|
+
</div>
|
@@ -1,17 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
<span class="page prev"><%== link.call(pagy.prev, '◄', 'aria-label="previous"') %></span>
|
5
|
-
<% else %>
|
6
|
-
<span class="page prev disabled">
|
7
|
-
◄
|
8
|
-
</span>
|
1
|
+
<div class="error-occurrences__nav">
|
2
|
+
<%= link_to pagy_url_for(1, pagy) do %>
|
3
|
+
<%= button_tag 'First', class: %w[button button-outline error-occurrences__nav-link], disabled: pagy.prev.nil? %>
|
9
4
|
<% end %>
|
10
|
-
|
11
|
-
<
|
12
|
-
<% else %>
|
13
|
-
<span class="page next disabled">
|
14
|
-
►
|
15
|
-
</span>
|
5
|
+
<%= link_to pagy_url_for(pagy.prev, pagy) do %>
|
6
|
+
<%= button_tag '<', class: %w[button button-outline error-occurrences__nav-link], disabled: pagy.prev.nil? %>
|
16
7
|
<% end %>
|
17
|
-
|
8
|
+
<div class="error-occurrences__nav-current">
|
9
|
+
<%= occurred_at %> (<%= pagy.page %>/<%= pagy.last %>)
|
10
|
+
</div>
|
11
|
+
<%= link_to pagy_url_for(pagy.next, pagy) do %>
|
12
|
+
<%= button_tag '>', class: %w[button button-outline error-occurrences__nav-link], disabled: pagy.next.nil? %>
|
13
|
+
<% end %>
|
14
|
+
<%= link_to pagy_url_for(pagy.last, pagy) do %>
|
15
|
+
<%= button_tag 'Last', class: %w[button button-outline error-occurrences__nav-link], disabled: pagy.next.nil? %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
@@ -1,33 +1,69 @@
|
|
1
|
-
<div class="row">
|
2
|
-
<div class="column column-
|
3
|
-
|
1
|
+
<div class="row error-occurrence__header">
|
2
|
+
<div class="column column-10">
|
3
|
+
<% @error.tags.each do|tag| %>
|
4
|
+
<div class="error-cell__tags">
|
5
|
+
<span class="error-tag"><%= tag %></span>
|
6
|
+
</div>
|
7
|
+
<% end %>
|
8
|
+
</div>
|
9
|
+
<div class="column column-80">
|
10
|
+
<div class="error-title">
|
11
|
+
<%= @error.class_name %>: <%= @error.message %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
<div class="column column-10">
|
15
|
+
<%= button_to('Resolve', resolved_errors_path(error_group: { id: @error.error_group_id }),
|
16
|
+
method: :post,
|
17
|
+
class: %w[button resolve-button],
|
18
|
+
data: { confirm: 'Are you sure you want to resolve this error?' }) %>
|
4
19
|
</div>
|
5
20
|
</div>
|
6
21
|
|
7
|
-
<div class="
|
8
|
-
|
9
|
-
|
10
|
-
<
|
11
|
-
|
22
|
+
<div class="row">
|
23
|
+
<div class="column column-50">
|
24
|
+
<ul data-tabs class="errors-tabs">
|
25
|
+
<li class="errors-tab">
|
26
|
+
<a data-tabby-default href="#summary">
|
27
|
+
<div class="errors-tab__content">
|
28
|
+
Summary
|
29
|
+
</div>
|
30
|
+
</a>
|
31
|
+
</li>
|
32
|
+
<li class="errors-tab">
|
33
|
+
<a href="#backtrace">
|
34
|
+
<div class="errors-tab__content">
|
35
|
+
Backtrace
|
36
|
+
</div>
|
37
|
+
</a>
|
38
|
+
</li>
|
39
|
+
<li class="errors-tab">
|
40
|
+
<a href="#user-data">
|
41
|
+
<div class="errors-tab__content">
|
42
|
+
User Data
|
43
|
+
</div>
|
44
|
+
</a>
|
45
|
+
</li>
|
46
|
+
</ul>
|
47
|
+
</div>
|
48
|
+
<div class="column column-50">
|
49
|
+
<%= render partial: 'exception_hunter/errors/pagy/pagy_nav', locals: { pagy: @pagy, occurred_at: @error.occurred_at } %>
|
50
|
+
</div>
|
12
51
|
</div>
|
13
52
|
|
14
|
-
<ul data-tabs>
|
15
|
-
<li><a data-tabby-default href="#summary">Summary</a></li>
|
16
|
-
<li><a href="#backtrace">Backtrace</a></li>
|
17
|
-
<li><a href="#user-data">User Data</a></li>
|
18
|
-
</ul>
|
19
53
|
|
20
|
-
<div class="
|
21
|
-
<div
|
22
|
-
|
23
|
-
|
54
|
+
<div class="errors__container">
|
55
|
+
<div class="tab-content">
|
56
|
+
<div id="summary">
|
57
|
+
<%= render partial: 'exception_hunter/errors/error_summary', locals: { error: @error } %>
|
58
|
+
</div>
|
24
59
|
|
25
|
-
|
26
|
-
|
27
|
-
|
60
|
+
<div id="backtrace">
|
61
|
+
<%= render partial: 'exception_hunter/errors/error_backtrace', locals: { error: @error } %>
|
62
|
+
</div>
|
28
63
|
|
29
|
-
|
30
|
-
|
64
|
+
<div id="user-data">
|
65
|
+
<%= render partial: 'exception_hunter/errors/error_user_data', locals: { error: @error } %>
|
66
|
+
</div>
|
31
67
|
</div>
|
32
68
|
</div>
|
33
69
|
|
@@ -7,12 +7,11 @@
|
|
7
7
|
|
8
8
|
<%= favicon_link_tag 'exception_hunter/logo.png' %>
|
9
9
|
|
10
|
-
<link rel="stylesheet" href="
|
10
|
+
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
11
11
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.css">
|
12
12
|
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.css">
|
13
13
|
|
14
14
|
<!-- Get patch fixes within a minor version -->
|
15
|
-
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/cferdinandi/tabby@12.0/dist/css/tabby-ui.min.css">
|
16
15
|
<script src="https://cdn.jsdelivr.net/gh/cferdinandi/tabby@12.0/dist/js/tabby.polyfills.min.js"></script>
|
17
16
|
|
18
17
|
<%= stylesheet_link_tag "exception_hunter/application", media: "all" %>
|
@@ -22,18 +21,80 @@
|
|
22
21
|
<div class="wrapper">
|
23
22
|
<nav class="nav">
|
24
23
|
<div class="container container__nav">
|
25
|
-
<div class="
|
26
|
-
|
27
|
-
<%=
|
28
|
-
|
24
|
+
<div class="row">
|
25
|
+
<div class="column column-40">
|
26
|
+
<%= link_to errors_path do %>
|
27
|
+
<div class="nav__title">
|
28
|
+
Exception Hunter / <%= application_name %>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
31
|
+
</div>
|
32
|
+
<div class="column column-10 column-offset-50">
|
33
|
+
<div class="logout">
|
34
|
+
<% if current_admin_user? %>
|
35
|
+
<div class="sign_out">
|
36
|
+
<%= link_to exception_hunter_logout_path do %>
|
37
|
+
Sign out
|
38
|
+
<% end %>
|
39
|
+
</div>
|
40
|
+
<% end %>
|
41
|
+
</div>
|
42
|
+
</div>
|
29
43
|
</div>
|
30
44
|
</div>
|
31
45
|
</nav>
|
32
46
|
|
33
47
|
<div class="container">
|
48
|
+
<% if flash[:notice] %>
|
49
|
+
<div id="flash-notice" class="flash flash--notice row">
|
50
|
+
<div class="column column-80">
|
51
|
+
<%= flash[:notice] %>
|
52
|
+
</div>
|
53
|
+
<div class="column column-20">
|
54
|
+
<div class="button button-clear button-dismiss" data-dismiss="#flash-notice">
|
55
|
+
<%= ['Cool!', 'Nice!', 'Ok', 'Dismiss', 'Fine. Whatever.', 'I know that'].sample %>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
<% end %>
|
60
|
+
|
34
61
|
<%= yield %>
|
35
62
|
</div>
|
63
|
+
|
64
|
+
<div class="footer">
|
65
|
+
Made with ♥ at <a href="https://www.rootstrap.com/" class="text--underline">Rootstrap</a>
|
66
|
+
</div>
|
36
67
|
</div>
|
37
68
|
|
69
|
+
<script type="text/javascript" charset="utf-8">
|
70
|
+
function confirmable(element) {
|
71
|
+
const message = element.getAttribute('data-confirm')
|
72
|
+
element.form.addEventListener('submit', confirm(message))
|
73
|
+
|
74
|
+
function confirm(message) {
|
75
|
+
return function (event) {
|
76
|
+
if (!window.confirm(message)) {
|
77
|
+
event.preventDefault()
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
function dismissible(element) {
|
84
|
+
element.addEventListener('click', dismiss(element))
|
85
|
+
|
86
|
+
function dismiss(element) {
|
87
|
+
return function () {
|
88
|
+
const selector = element.getAttribute('data-dismiss')
|
89
|
+
const elementToDismiss = document.querySelector(selector)
|
90
|
+
elementToDismiss.remove()
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
Array.from(document.querySelectorAll('[data-confirm]')).forEach(confirmable)
|
96
|
+
Array.from(document.querySelectorAll('[data-dismiss]')).forEach(dismissible)
|
97
|
+
</script>
|
98
|
+
|
38
99
|
</body>
|
39
100
|
</html>
|