ideasbugs 0.7.3 → 0.7.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/app/controllers/ideasbugs/feedbacks_controller.rb +2 -0
- data/app/views/ideasbugs/feedbacks/_feedback_panel.html.erb +65 -0
- data/app/views/ideasbugs/feedbacks/index.html.erb +89 -72
- data/app/views/ideasbugs/feedbacks/show.html.erb +3 -58
- data/app/views/layouts/ideasbugs/application.html.erb +1 -1
- data/lib/ideasbugs/dashboard.css +83 -1
- data/lib/ideasbugs/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd02a4b6d94f04c5fe4533f1f9fe3e51a8d7d5a0d9508657fe0b01fc2ac83abe
|
|
4
|
+
data.tar.gz: 6308208667e3d63aff2ec4750811e2a781ce06722ce54b8f4b44df0dba373171
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9b8082e850661015f81cbc9b0700f78a1e0eb2d4e0d155b75e761640270f01b5d670f219ac464a8c28346be7a6c4cc7d0d4acf88001fb4374cfe235abd6c228
|
|
7
|
+
data.tar.gz: 5f7f874294bf4bd272aa1e31712e36595f7aa5911ce2ca6a1c5cf11be619d3f12616c7288b082aaea6f73d9c91f55ad269f5f3b015e1d0258ecc7d2cf3f47fef
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.4 (2026-07-31)
|
|
4
|
+
|
|
5
|
+
- Redesigned the admin feedback dashboard into a two-column triage layout with
|
|
6
|
+
status filters, a selected-record pane, and refreshed before/after
|
|
7
|
+
screenshots.
|
|
8
|
+
- Kept standalone feedback show pages working independently, including
|
|
9
|
+
scrollable detail content on narrow screens.
|
|
10
|
+
|
|
3
11
|
## 0.7.3 (2026-07-31)
|
|
4
12
|
|
|
5
13
|
- Added `mount_ideasbugs at: "/feedback"` as the install-time route helper,
|
|
@@ -36,6 +36,8 @@ module Ideasbugs
|
|
|
36
36
|
@more = @feedbacks.size > PER_PAGE
|
|
37
37
|
@feedbacks = @feedbacks.first(PER_PAGE)
|
|
38
38
|
|
|
39
|
+
@selected_feedback = tenant_scope.find_by(id: params[:feedback_id]) if params[:feedback_id].present?
|
|
40
|
+
|
|
39
41
|
# Only surface the Section column when it can carry information: the host
|
|
40
42
|
# configured sections, or some record already has one (e.g. sections were
|
|
41
43
|
# configured historically). Otherwise it's a permanently blank column.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<section class="feedback-panel">
|
|
2
|
+
<div class="panel-head">
|
|
3
|
+
<h1>
|
|
4
|
+
<span class="badge kind-<%= feedback.kind.to_s.parameterize %>">
|
|
5
|
+
<%= t("ideasbugs.kinds.#{feedback.kind}", default: feedback.kind.humanize) %>
|
|
6
|
+
</span>
|
|
7
|
+
<span class="badge status-<%= feedback.status %>">
|
|
8
|
+
<%= t("ideasbugs.statuses.#{feedback.status}", default: feedback.status.humanize) %>
|
|
9
|
+
</span>
|
|
10
|
+
<span class="case-id">#<%= feedback.id %></span>
|
|
11
|
+
</h1>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="detail-scroll">
|
|
15
|
+
<div class="card pad">
|
|
16
|
+
<dl>
|
|
17
|
+
<% if feedback.section.present? %>
|
|
18
|
+
<dt><%= t('ideasbugs.section', default: 'Section') %></dt>
|
|
19
|
+
<dd><%= feedback.section %></dd>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% if feedback.author_label.present? || feedback.author_id.present? %>
|
|
22
|
+
<dt><%= t('ideasbugs.dashboard.from', default: 'From') %></dt>
|
|
23
|
+
<dd><%= feedback.author_label.presence || "##{feedback.author_id}" %></dd>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% if feedback.page_url.present? %>
|
|
26
|
+
<dt><%= t('ideasbugs.dashboard.page', default: 'Page') %></dt>
|
|
27
|
+
<dd><%= link_to feedback.page_url, feedback.page_url, target: '_blank', rel: 'noopener' %></dd>
|
|
28
|
+
<% end %>
|
|
29
|
+
<% if feedback.user_agent.present? %>
|
|
30
|
+
<dt><%= t('ideasbugs.dashboard.browser', default: 'Browser') %></dt>
|
|
31
|
+
<dd class="muted"><%= feedback.user_agent %></dd>
|
|
32
|
+
<% end %>
|
|
33
|
+
<dt><%= t('ideasbugs.dashboard.filed', default: 'Filed') %></dt>
|
|
34
|
+
<dd><%= feedback.created_at %></dd>
|
|
35
|
+
</dl>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="card pad">
|
|
39
|
+
<p class="message"><%= feedback.message %></p>
|
|
40
|
+
|
|
41
|
+
<% if feedback.screenshots? %>
|
|
42
|
+
<div class="shots">
|
|
43
|
+
<% feedback.screenshots.each do |screenshot| %>
|
|
44
|
+
<%= link_to feedback_screenshot_path(feedback, screenshot.id), target: '_blank', rel: 'noopener' do %>
|
|
45
|
+
<%= image_tag feedback_screenshot_path(feedback, screenshot.id), alt: screenshot.filename %>
|
|
46
|
+
<% end %>
|
|
47
|
+
<% end %>
|
|
48
|
+
</div>
|
|
49
|
+
<% end %>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div class="actions">
|
|
53
|
+
<% (Ideasbugs::Feedback::STATUSES - [feedback.status]).each do |status| %>
|
|
54
|
+
<%= button_to t("ideasbugs.dashboard.move_to_#{status}", default: "Mark #{status.humanize.downcase}"),
|
|
55
|
+
feedback_path(feedback),
|
|
56
|
+
method: :patch, params: { feedback: { status: status } },
|
|
57
|
+
class: ('primary' if status == 'resolved') %>
|
|
58
|
+
<% end %>
|
|
59
|
+
<%= button_to t('ideasbugs.dashboard.delete', default: 'Delete'),
|
|
60
|
+
feedback_path(feedback),
|
|
61
|
+
method: :delete, class: 'danger',
|
|
62
|
+
form: { onsubmit: "return confirm('#{t('ideasbugs.dashboard.delete_confirm', default: 'Delete this feedback?')}')" } %>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</section>
|
|
@@ -1,82 +1,99 @@
|
|
|
1
|
+
<% content_for :body_class, 'ib-index' %>
|
|
2
|
+
|
|
1
3
|
<h1><%= t('ideasbugs.dashboard.title', default: 'IdeasBugs') %></h1>
|
|
2
4
|
|
|
3
|
-
<div class="
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
<div class="triage-shell <%= 'has-selected' if @selected_feedback %>">
|
|
6
|
+
<aside class="triage-sidebar" aria-label="<%= t('ideasbugs.dashboard.feedback', default: 'Feedback') %>">
|
|
7
|
+
<div class="tabs">
|
|
8
|
+
<% Ideasbugs::Feedback::STATUSES.each do |status| %>
|
|
9
|
+
<%= link_to feedbacks_path(status: status, kind: @kind, q: @query),
|
|
10
|
+
class: ('active' if status == @status) do %>
|
|
11
|
+
<%= t("ideasbugs.statuses.#{status}", default: status.humanize) %>
|
|
12
|
+
<span class="count"><%= @counts.fetch(status, 0) %></span>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
12
16
|
|
|
13
|
-
<form class="filters" method="get">
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</form>
|
|
17
|
+
<form class="filters" method="get">
|
|
18
|
+
<input type="hidden" name="status" value="<%= @status %>">
|
|
19
|
+
<select name="kind" onchange="this.form.submit()">
|
|
20
|
+
<option value=""><%= t('ideasbugs.dashboard.all_kinds', default: 'All types') %></option>
|
|
21
|
+
<% Ideasbugs.config.kinds.each do |kind| %>
|
|
22
|
+
<option value="<%= kind %>" <%= 'selected' if kind.to_s == @kind %>>
|
|
23
|
+
<%= t("ideasbugs.kinds.#{kind}", default: kind.to_s.humanize) %>
|
|
24
|
+
</option>
|
|
25
|
+
<% end %>
|
|
26
|
+
</select>
|
|
27
|
+
<input type="search" name="q" value="<%= @query %>"
|
|
28
|
+
placeholder="<%= t('ideasbugs.dashboard.search_placeholder', default: 'Search message, author, section…') %>"
|
|
29
|
+
aria-label="<%= t('ideasbugs.dashboard.search', default: 'Search') %>">
|
|
30
|
+
<button><%= t('ideasbugs.dashboard.search', default: 'Search') %></button>
|
|
31
|
+
</form>
|
|
28
32
|
|
|
29
|
-
<div class="
|
|
30
|
-
|
|
31
|
-
<table>
|
|
32
|
-
<thead>
|
|
33
|
-
<tr>
|
|
34
|
-
<th><%= t('ideasbugs.kind', default: 'Type') %></th>
|
|
35
|
-
<th><%= t('ideasbugs.dashboard.message', default: 'Message') %></th>
|
|
36
|
-
<% if @show_section %>
|
|
37
|
-
<th><%= t('ideasbugs.section', default: 'Section') %></th>
|
|
38
|
-
<% end %>
|
|
39
|
-
<th><%= t('ideasbugs.dashboard.from', default: 'From') %></th>
|
|
40
|
-
<th><%= t('ideasbugs.dashboard.filed', default: 'Filed') %></th>
|
|
41
|
-
</tr>
|
|
42
|
-
</thead>
|
|
43
|
-
<tbody>
|
|
33
|
+
<div class="feedback-list">
|
|
34
|
+
<% if @feedbacks.any? %>
|
|
44
35
|
<% @feedbacks.each do |feedback| %>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
<%= link_to feedbacks_path(status: @status, kind: @kind, q: @query, page: @page,
|
|
37
|
+
feedback_id: feedback.id),
|
|
38
|
+
class: ['feedback-row', ('active' if @selected_feedback&.id == feedback.id)].compact do %>
|
|
39
|
+
<span class="feedback-main">
|
|
40
|
+
<span class="feedback-topline">
|
|
41
|
+
<span class="badge kind-<%= feedback.kind.to_s.parameterize %>">
|
|
42
|
+
<%= t("ideasbugs.kinds.#{feedback.kind}", default: feedback.kind.humanize) %>
|
|
43
|
+
</span>
|
|
44
|
+
<span class="badge status-<%= feedback.status %>">
|
|
45
|
+
<%= t("ideasbugs.statuses.#{feedback.status}", default: feedback.status.humanize) %>
|
|
46
|
+
</span>
|
|
47
|
+
<span class="muted feedback-time" title="<%= feedback.created_at %>">
|
|
48
|
+
<%= time_ago_in_words(feedback.created_at) %>
|
|
49
|
+
</span>
|
|
50
|
+
</span>
|
|
51
|
+
<span class="feedback-message"><%= truncate(feedback.message, length: 120) %></span>
|
|
52
|
+
<span class="muted feedback-meta">
|
|
53
|
+
<span>#<%= feedback.id %></span>
|
|
54
|
+
<% if @show_section && feedback.section.present? %>
|
|
55
|
+
<span class="feedback-section"><%= feedback.section %></span>
|
|
56
|
+
<% end %>
|
|
57
|
+
<% if feedback.author_label.present? %>
|
|
58
|
+
<span><%= feedback.author_label %></span>
|
|
59
|
+
<% end %>
|
|
49
60
|
</span>
|
|
50
|
-
</
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<% if feedback.screenshots? %>
|
|
54
|
-
<div class="muted">📎 <%= feedback.screenshots.count %></div>
|
|
55
|
-
<% end %>
|
|
56
|
-
</td>
|
|
57
|
-
<% if @show_section %>
|
|
58
|
-
<td><%= feedback.section %></td>
|
|
61
|
+
</span>
|
|
62
|
+
<% if feedback.screenshots? %>
|
|
63
|
+
<span class="muted attachment-count">📎 <%= feedback.screenshots.count %></span>
|
|
59
64
|
<% end %>
|
|
60
|
-
|
|
61
|
-
<td class="muted" title="<%= feedback.created_at %>">
|
|
62
|
-
<%= time_ago_in_words(feedback.created_at) %>
|
|
63
|
-
</td>
|
|
64
|
-
</tr>
|
|
65
|
+
<% end %>
|
|
65
66
|
<% end %>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
<% else %>
|
|
68
|
+
<div class="empty"><%= t('ideasbugs.dashboard.empty', default: 'Nothing here yet.') %></div>
|
|
69
|
+
<% end %>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="pager">
|
|
73
|
+
<% if @page > 1 %>
|
|
74
|
+
<%= link_to t('ideasbugs.dashboard.newer', default: '← Newer'),
|
|
75
|
+
feedbacks_path(status: @status, kind: @kind, q: @query, page: @page - 1) %>
|
|
76
|
+
<% end %>
|
|
77
|
+
<% if @more %>
|
|
78
|
+
<%= link_to t('ideasbugs.dashboard.older', default: 'Older →'),
|
|
79
|
+
feedbacks_path(status: @status, kind: @kind, q: @query, page: @page + 1) %>
|
|
80
|
+
<% end %>
|
|
81
|
+
</div>
|
|
82
|
+
</aside>
|
|
72
83
|
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
<main class="triage-detail" aria-label="<%= t('ideasbugs.dashboard.current_feedback', default: 'Current feedback') %>">
|
|
85
|
+
<% if @selected_feedback %>
|
|
86
|
+
<p class="mobile-back">
|
|
87
|
+
<%= link_to t('ideasbugs.dashboard.back', default: '← All feedback'),
|
|
88
|
+
feedbacks_path(status: @status, kind: @kind, q: @query, page: @page) %>
|
|
89
|
+
</p>
|
|
90
|
+
<%= render 'feedback_panel', feedback: @selected_feedback %>
|
|
91
|
+
<% else %>
|
|
92
|
+
<div class="empty-state">
|
|
93
|
+
<h2><%= t('ideasbugs.dashboard.select_feedback', default: 'Select feedback') %></h2>
|
|
94
|
+
<p class="muted"><%= t('ideasbugs.dashboard.select_feedback_hint',
|
|
95
|
+
default: 'Open an item from the list to triage it without leaving the queue.') %></p>
|
|
96
|
+
</div>
|
|
97
|
+
<% end %>
|
|
98
|
+
</main>
|
|
82
99
|
</div>
|
|
@@ -1,63 +1,8 @@
|
|
|
1
|
+
<% content_for :body_class, 'ib-show' %>
|
|
2
|
+
|
|
1
3
|
<div class="breadcrumb">
|
|
2
4
|
<%= link_to t('ideasbugs.dashboard.title', default: 'IdeasBugs'), root_path %>
|
|
3
5
|
/ #<%= @feedback.id %>
|
|
4
6
|
</div>
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
<span class="badge kind-<%= @feedback.kind.to_s.parameterize %>">
|
|
8
|
-
<%= t("ideasbugs.kinds.#{@feedback.kind}", default: @feedback.kind.humanize) %>
|
|
9
|
-
</span>
|
|
10
|
-
<span class="badge status-<%= @feedback.status %>">
|
|
11
|
-
<%= t("ideasbugs.statuses.#{@feedback.status}", default: @feedback.status.humanize) %>
|
|
12
|
-
</span>
|
|
13
|
-
</h1>
|
|
14
|
-
|
|
15
|
-
<div class="card" style="padding: 16px; margin-bottom: 16px;">
|
|
16
|
-
<p class="message"><%= @feedback.message %></p>
|
|
17
|
-
|
|
18
|
-
<% if @feedback.screenshots? %>
|
|
19
|
-
<div class="shots">
|
|
20
|
-
<% @feedback.screenshots.each do |screenshot| %>
|
|
21
|
-
<%= link_to feedback_screenshot_path(@feedback, screenshot.id), target: '_blank', rel: 'noopener' do %>
|
|
22
|
-
<%= image_tag feedback_screenshot_path(@feedback, screenshot.id), alt: screenshot.filename %>
|
|
23
|
-
<% end %>
|
|
24
|
-
<% end %>
|
|
25
|
-
</div>
|
|
26
|
-
<% end %>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<div class="card" style="padding: 16px;">
|
|
30
|
-
<dl>
|
|
31
|
-
<% if @feedback.section.present? %>
|
|
32
|
-
<dt><%= t('ideasbugs.section', default: 'Section') %></dt>
|
|
33
|
-
<dd><%= @feedback.section %></dd>
|
|
34
|
-
<% end %>
|
|
35
|
-
<% if @feedback.author_label.present? || @feedback.author_id.present? %>
|
|
36
|
-
<dt><%= t('ideasbugs.dashboard.from', default: 'From') %></dt>
|
|
37
|
-
<dd><%= @feedback.author_label.presence || "##{@feedback.author_id}" %></dd>
|
|
38
|
-
<% end %>
|
|
39
|
-
<% if @feedback.page_url.present? %>
|
|
40
|
-
<dt><%= t('ideasbugs.dashboard.page', default: 'Page') %></dt>
|
|
41
|
-
<dd><%= link_to @feedback.page_url, @feedback.page_url, target: '_blank', rel: 'noopener' %></dd>
|
|
42
|
-
<% end %>
|
|
43
|
-
<% if @feedback.user_agent.present? %>
|
|
44
|
-
<dt><%= t('ideasbugs.dashboard.browser', default: 'Browser') %></dt>
|
|
45
|
-
<dd class="muted"><%= @feedback.user_agent %></dd>
|
|
46
|
-
<% end %>
|
|
47
|
-
<dt><%= t('ideasbugs.dashboard.filed', default: 'Filed') %></dt>
|
|
48
|
-
<dd><%= @feedback.created_at %></dd>
|
|
49
|
-
</dl>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<div class="actions">
|
|
53
|
-
<% (Ideasbugs::Feedback::STATUSES - [@feedback.status]).each do |status| %>
|
|
54
|
-
<%= button_to t("ideasbugs.dashboard.move_to_#{status}", default: "Mark #{status.humanize.downcase}"),
|
|
55
|
-
feedback_path(@feedback),
|
|
56
|
-
method: :patch, params: { feedback: { status: status } },
|
|
57
|
-
class: ('primary' if status == 'resolved') %>
|
|
58
|
-
<% end %>
|
|
59
|
-
<%= button_to t('ideasbugs.dashboard.delete', default: 'Delete'),
|
|
60
|
-
feedback_path(@feedback),
|
|
61
|
-
method: :delete, class: 'danger',
|
|
62
|
-
form: { onsubmit: "return confirm('#{t('ideasbugs.dashboard.delete_confirm', default: 'Delete this feedback?')}')" } %>
|
|
63
|
-
</div>
|
|
8
|
+
<%= render 'feedback_panel', feedback: @feedback %>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<%= stylesheet_link_tag "#{dashboard_stylesheet_path}?v=#{Ideasbugs::Widget.dashboard_stylesheet_fingerprint}",
|
|
9
9
|
'data-turbo-track': 'reload' %>
|
|
10
10
|
</head>
|
|
11
|
-
<body>
|
|
11
|
+
<body class="<%= content_for?(:body_class) ? yield(:body_class) : '' %>">
|
|
12
12
|
<div class="container">
|
|
13
13
|
<%= yield %>
|
|
14
14
|
</div>
|
data/lib/ideasbugs/dashboard.css
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
border-radius: 8px; background: var(--surface); color: var(--text); font: inherit; }
|
|
31
31
|
.filters input[type=search] { flex: 1; max-width: 320px; }
|
|
32
32
|
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
|
|
33
|
+
.card.pad { padding: 16px; }
|
|
33
34
|
table { width: 100%; border-collapse: collapse; }
|
|
34
35
|
th, td { padding: 10px 14px; text-align: left; vertical-align: top; }
|
|
35
36
|
th { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
background: var(--surface); color: var(--text); font: inherit; }
|
|
52
53
|
button.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }
|
|
53
54
|
button.danger { color: var(--bug); }
|
|
54
|
-
.message { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
55
|
+
.message { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
55
56
|
dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 16px; margin: 0; }
|
|
56
57
|
dt { color: var(--muted); }
|
|
57
58
|
dd { margin: 0; overflow-wrap: anywhere; }
|
|
@@ -59,3 +60,84 @@
|
|
|
59
60
|
.shots img { max-width: 280px; max-height: 200px; border: 1px solid var(--border); border-radius: 8px; }
|
|
60
61
|
.empty { padding: 48px 16px; text-align: center; color: var(--muted); }
|
|
61
62
|
.breadcrumb { margin-bottom: 12px; font-size: 13px; }
|
|
63
|
+
.case-id { color: var(--muted); font-weight: 400; font-size: 15px; }
|
|
64
|
+
.panel-head { flex: 0 0 auto; }
|
|
65
|
+
.panel-head h1 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
66
|
+
.detail-scroll { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
|
|
67
|
+
.feedback-panel { min-width: 0; display: flex; flex-direction: column; }
|
|
68
|
+
.feedback-panel .actions { flex: 0 0 auto; }
|
|
69
|
+
.feedback-panel .detail-scroll > .actions { margin: 0; }
|
|
70
|
+
|
|
71
|
+
/* Desktop triage: list on the left, selected feedback on the right. */
|
|
72
|
+
.ib-index, .ib-index .container { height: 100vh; height: 100dvh; }
|
|
73
|
+
.ib-index { overflow: hidden; }
|
|
74
|
+
.ib-index .container { max-width: 1280px; display: flex; flex-direction: column; padding-bottom: 16px; }
|
|
75
|
+
.ib-index .container > h1 { flex: 0 0 auto; margin-bottom: 14px; }
|
|
76
|
+
.triage-shell { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: minmax(320px, 400px) 1fr;
|
|
77
|
+
gap: 16px; }
|
|
78
|
+
.triage-sidebar { min-width: 0; min-height: 0; display: flex; flex-direction: column;
|
|
79
|
+
background: var(--surface); border: 1px solid var(--border);
|
|
80
|
+
border-radius: 8px; overflow: hidden; }
|
|
81
|
+
.triage-sidebar .tabs { flex: 0 0 auto; margin: 12px 12px 0; padding: 0; background: transparent;
|
|
82
|
+
border: 0; border-radius: 0; gap: 6px; flex-wrap: nowrap; }
|
|
83
|
+
.triage-sidebar .tabs a { flex: 1 1 0; position: relative; display: flex; align-items: center;
|
|
84
|
+
justify-content: center; gap: 6px; min-height: 34px; padding: 5px 10px 8px;
|
|
85
|
+
border-radius: 8px; color: var(--muted); font-weight: 600; }
|
|
86
|
+
.triage-sidebar .tabs a.active { color: var(--text); border: 0; background: transparent; box-shadow: none; margin: 0; }
|
|
87
|
+
.triage-sidebar .tabs a.active::after { content: ""; position: absolute; left: 20px; right: 20px; bottom: 0;
|
|
88
|
+
height: 2px; border-radius: 999px; background: var(--accent); }
|
|
89
|
+
.triage-sidebar .tabs a:not(.active):hover { text-decoration: none; color: var(--text); background: var(--bg); }
|
|
90
|
+
.triage-sidebar .tabs .count { margin-left: 0; background: color-mix(in srgb, var(--muted) 14%, transparent); }
|
|
91
|
+
.triage-sidebar .tabs a.active .count { background: var(--border); }
|
|
92
|
+
.triage-sidebar .filters { flex: 0 0 auto; display: grid; grid-template-columns: minmax(0, 1fr) auto;
|
|
93
|
+
margin: 0; padding: 12px; border-bottom: 1px solid var(--border); }
|
|
94
|
+
.triage-sidebar .filters select { grid-column: 1 / -1; width: 100%; }
|
|
95
|
+
.triage-sidebar .filters input[type=search] { min-width: 0; max-width: none; }
|
|
96
|
+
.feedback-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
|
|
97
|
+
.feedback-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; padding: 13px 14px;
|
|
98
|
+
color: var(--text); border-bottom: 1px solid var(--border); }
|
|
99
|
+
.feedback-row:hover { text-decoration: none; background: var(--bg); }
|
|
100
|
+
.feedback-row.active { background: color-mix(in srgb, var(--accent) 9%, var(--surface));
|
|
101
|
+
box-shadow: inset 3px 0 0 var(--accent); }
|
|
102
|
+
.feedback-main { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
|
|
103
|
+
.feedback-topline, .feedback-meta { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
104
|
+
.feedback-message, .feedback-meta span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
105
|
+
.feedback-message { display: block; font-weight: 600; }
|
|
106
|
+
.feedback-time { margin-left: auto; white-space: nowrap; }
|
|
107
|
+
.attachment-count { align-self: start; white-space: nowrap; }
|
|
108
|
+
.triage-sidebar .pager { flex: 0 0 auto; margin: 0; padding: 10px 12px; border-top: 1px solid var(--border); }
|
|
109
|
+
.triage-detail { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
110
|
+
.triage-detail .feedback-panel { flex: 1 1 auto; min-height: 0; }
|
|
111
|
+
.triage-detail .detail-scroll { flex: 1 1 auto; overflow-y: auto; padding-right: 2px; }
|
|
112
|
+
.triage-detail .actions { margin-bottom: 0; }
|
|
113
|
+
.empty-state { margin: auto; max-width: 320px; padding: 24px; text-align: center; }
|
|
114
|
+
.empty-state h2 { margin: 0 0 6px; font-size: 18px; }
|
|
115
|
+
.empty-state p { margin: 0; }
|
|
116
|
+
.mobile-back { display: none; }
|
|
117
|
+
|
|
118
|
+
/* Standalone show pages keep normal page scrolling. */
|
|
119
|
+
.ib-show { min-height: 100vh; overflow: auto; }
|
|
120
|
+
.ib-show .feedback-panel { gap: 0; }
|
|
121
|
+
.ib-show .detail-scroll { overflow: visible; }
|
|
122
|
+
@media (max-width: 760px) {
|
|
123
|
+
body { font-size: 14px; }
|
|
124
|
+
.container { padding: 14px 10px 24px; }
|
|
125
|
+
.ib-index, .ib-show { overflow-x: hidden; }
|
|
126
|
+
.feedback-panel, .detail-scroll, .card, dl, dd { min-width: 0; max-width: 100%; }
|
|
127
|
+
.feedback-panel .card { width: calc(100vw - 20px); }
|
|
128
|
+
.message, dd, .muted { max-width: calc(100vw - 54px); }
|
|
129
|
+
.ib-index { overflow-y: auto; overflow-x: hidden; }
|
|
130
|
+
.ib-index, .ib-index .container { min-height: 100vh; height: auto; }
|
|
131
|
+
.ib-index .container { padding-bottom: 10px; }
|
|
132
|
+
.triage-shell { display: block; min-height: calc(100vh - 62px); }
|
|
133
|
+
.triage-sidebar, .triage-detail { min-height: calc(100vh - 62px); }
|
|
134
|
+
.triage-shell.has-selected .triage-sidebar { display: none; }
|
|
135
|
+
.triage-shell:not(.has-selected) .triage-detail { display: none; }
|
|
136
|
+
.feedback-list { overflow: visible; }
|
|
137
|
+
.feedback-row { padding: 12px; }
|
|
138
|
+
.feedback-meta { display: none; }
|
|
139
|
+
.triage-detail .feedback-panel { min-height: calc(100vh - 64px); }
|
|
140
|
+
.mobile-back { display: block; flex: 0 0 auto; margin: 10px 10px 0; font-size: 13px; }
|
|
141
|
+
.panel-head h1 { font-size: 18px; }
|
|
142
|
+
dl { grid-template-columns: 1fr; gap: 3px; }
|
|
143
|
+
}
|
data/lib/ideasbugs/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ideasbugs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Shmarov
|
|
@@ -45,6 +45,7 @@ files:
|
|
|
45
45
|
- app/helpers/ideasbugs/widget_helper.rb
|
|
46
46
|
- app/models/ideasbugs/application_record.rb
|
|
47
47
|
- app/models/ideasbugs/feedback.rb
|
|
48
|
+
- app/views/ideasbugs/feedbacks/_feedback_panel.html.erb
|
|
48
49
|
- app/views/ideasbugs/feedbacks/index.html.erb
|
|
49
50
|
- app/views/ideasbugs/feedbacks/show.html.erb
|
|
50
51
|
- app/views/layouts/ideasbugs/application.html.erb
|