rails-image-post-solution 0.1.0
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 +7 -0
- data/.rubocop.yml +8 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +264 -0
- data/Rakefile +8 -0
- data/app/controllers/admin/frozen_posts_controller.rb +79 -0
- data/app/controllers/admin/image_reports_controller.rb +81 -0
- data/app/controllers/admin/users_controller.rb +100 -0
- data/app/controllers/rails_image_post_solution/admin/image_reports_controller.rb +92 -0
- data/app/controllers/rails_image_post_solution/image_reports_controller.rb +87 -0
- data/app/jobs/rails_image_post_solution/image_moderation_job.rb +97 -0
- data/app/models/rails_image_post_solution/image_report.rb +57 -0
- data/app/services/rails_image_post_solution/openai_vision_service.rb +128 -0
- data/app/views/admin/frozen_posts/index.html.erb +128 -0
- data/app/views/admin/image_reports/index.html.erb +94 -0
- data/app/views/admin/image_reports/show.html.erb +138 -0
- data/app/views/admin/users/index.html.erb +93 -0
- data/app/views/admin/users/show.html.erb +198 -0
- data/app/views/rails_image_post_solution/admin/image_reports/index.html.erb +100 -0
- data/app/views/rails_image_post_solution/admin/image_reports/show.html.erb +154 -0
- data/app/views/shared/_image_report_button.html.erb +26 -0
- data/app/views/shared/_image_report_modal.html.erb +45 -0
- data/config/locales/en.yml +260 -0
- data/config/locales/ja.yml +259 -0
- data/config/routes.rb +16 -0
- data/db/migrate/20250101000001_create_rails_image_post_solution_image_reports.rb +20 -0
- data/db/migrate/20250101000002_add_ai_moderation_fields_to_image_reports.rb +12 -0
- data/lib/generators/rails_image_post_solution/install/README +27 -0
- data/lib/generators/rails_image_post_solution/install/install_generator.rb +47 -0
- data/lib/generators/rails_image_post_solution/install/templates/add_ai_moderation_fields.rb.erb +12 -0
- data/lib/generators/rails_image_post_solution/install/templates/create_image_reports.rb.erb +19 -0
- data/lib/generators/rails_image_post_solution/install/templates/rails_image_post_solution.rb +15 -0
- data/lib/rails_image_post_solution/engine.rb +22 -0
- data/lib/rails_image_post_solution/version.rb +5 -0
- data/lib/rails_image_post_solution.rb +28 -0
- metadata +150 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<div class="admin-container">
|
|
2
|
+
<h1 class="admin-header"><%= t('rails_image_post_solution.admin.index.title') %></h1>
|
|
3
|
+
|
|
4
|
+
<!-- Statistics -->
|
|
5
|
+
<div class="admin-stats">
|
|
6
|
+
<div class="stat-card">
|
|
7
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.total') %></div>
|
|
8
|
+
<div class="stat-value"><%= @stats[:total] %></div>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="stat-card pending">
|
|
11
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.pending') %></div>
|
|
12
|
+
<div class="stat-value"><%= @stats[:pending] %></div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="stat-card confirmed">
|
|
15
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.confirmed') %></div>
|
|
16
|
+
<div class="stat-value"><%= @stats[:confirmed] %></div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="stat-card dismissed">
|
|
19
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.dismissed') %></div>
|
|
20
|
+
<div class="stat-value"><%= @stats[:dismissed] %></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="stat-card reviewed">
|
|
23
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.reviewed') %></div>
|
|
24
|
+
<div class="stat-value"><%= @stats[:reviewed] %></div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Filters -->
|
|
29
|
+
<div class="admin-filters">
|
|
30
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.all'), admin_image_reports_path, class: "filter-btn #{'active' if @status_filter == 'all'}" %>
|
|
31
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.pending'), admin_image_reports_path(status: 'pending'), class: "filter-btn #{'active' if @status_filter == 'pending'}" %>
|
|
32
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.confirmed'), admin_image_reports_path(status: 'confirmed'), class: "filter-btn #{'active' if @status_filter == 'confirmed'}" %>
|
|
33
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.dismissed'), admin_image_reports_path(status: 'dismissed'), class: "filter-btn #{'active' if @status_filter == 'dismissed'}" %>
|
|
34
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.reviewed'), admin_image_reports_path(status: 'reviewed'), class: "filter-btn #{'active' if @status_filter == 'reviewed'}" %>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Reports List -->
|
|
38
|
+
<div class="admin-reports-list">
|
|
39
|
+
<% if @reports.any? %>
|
|
40
|
+
<table class="admin-table">
|
|
41
|
+
<thead>
|
|
42
|
+
<tr>
|
|
43
|
+
<th><%= t('rails_image_post_solution.admin.index.table.image') %></th>
|
|
44
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reporter') %></th>
|
|
45
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reason') %></th>
|
|
46
|
+
<th><%= t('rails_image_post_solution.admin.index.table.status') %></th>
|
|
47
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reported_at') %></th>
|
|
48
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reviewer') %></th>
|
|
49
|
+
<th><%= t('rails_image_post_solution.admin.index.table.actions') %></th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<tbody>
|
|
53
|
+
<% @reports.each do |report| %>
|
|
54
|
+
<tr class="report-row status-<%= report.status %>">
|
|
55
|
+
<td class="image-cell">
|
|
56
|
+
<% if report.active_storage_attachment.present? %>
|
|
57
|
+
<%= image_tag report.active_storage_attachment.blob, class: "admin-thumbnail", style: "max-width: 100px; max-height: 100px;" %>
|
|
58
|
+
<% else %>
|
|
59
|
+
<span class="text-muted"><%= t('rails_image_post_solution.admin.index.deleted') %></span>
|
|
60
|
+
<% end %>
|
|
61
|
+
</td>
|
|
62
|
+
<td><%= link_to report.user.display_name, user_path(report.user), class: "user-link" %></td>
|
|
63
|
+
<td class="reason-cell"><%= truncate(report.reason, length: 50) || t('rails_image_post_solution.admin.index.no_reason') %></td>
|
|
64
|
+
<td>
|
|
65
|
+
<span class="status-badge status-<%= report.status %>">
|
|
66
|
+
<%= t("rails_image_post_solution.image_report.statuses.#{report.status}") %>
|
|
67
|
+
</span>
|
|
68
|
+
</td>
|
|
69
|
+
<td><%= l(report.created_at, format: :long) %></td>
|
|
70
|
+
<td>
|
|
71
|
+
<% if report.reviewed_by %>
|
|
72
|
+
<%= report.reviewed_by.display_name %>
|
|
73
|
+
<% else %>
|
|
74
|
+
<span class="text-muted">-</span>
|
|
75
|
+
<% end %>
|
|
76
|
+
</td>
|
|
77
|
+
<td class="actions-cell">
|
|
78
|
+
<%= link_to t('rails_image_post_solution.admin.index.detail'), admin_image_report_path(report), class: "btn-action btn-detail" %>
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
<% end %>
|
|
82
|
+
</tbody>
|
|
83
|
+
</table>
|
|
84
|
+
<% else %>
|
|
85
|
+
<div class="empty-state">
|
|
86
|
+
<p><%= t('rails_image_post_solution.admin.index.no_reports') %></p>
|
|
87
|
+
</div>
|
|
88
|
+
<% end %>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="admin-actions">
|
|
92
|
+
<%= link_to t('rails_image_post_solution.admin.index.back'), root_path, class: "btn btn-secondary" %>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<div class="admin-container">
|
|
2
|
+
<h1 class="admin-header"><%= t('rails_image_post_solution.admin.show.title') %></h1>
|
|
3
|
+
|
|
4
|
+
<div class="report-detail">
|
|
5
|
+
<!-- Image Display -->
|
|
6
|
+
<div class="detail-section">
|
|
7
|
+
<h3><%= t('rails_image_post_solution.admin.show.reported_image') %></h3>
|
|
8
|
+
<% if @attachment.present? %>
|
|
9
|
+
<div class="reported-image-container">
|
|
10
|
+
<%= image_tag @attachment.blob, class: "reported-image-large", style: "max-width: 800px; max-height: 600px;" %>
|
|
11
|
+
</div>
|
|
12
|
+
<% else %>
|
|
13
|
+
<p class="text-muted"><%= t('rails_image_post_solution.admin.show.image_deleted') %></p>
|
|
14
|
+
<% end %>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- Report Information -->
|
|
18
|
+
<div class="detail-section">
|
|
19
|
+
<h3><%= t('rails_image_post_solution.admin.show.report_info') %></h3>
|
|
20
|
+
<table class="detail-table">
|
|
21
|
+
<tr>
|
|
22
|
+
<th><%= t('rails_image_post_solution.admin.show.status') %></th>
|
|
23
|
+
<td>
|
|
24
|
+
<span class="status-badge status-<%= @report.status %>">
|
|
25
|
+
<%= t("rails_image_post_solution.image_report.statuses.#{@report.status}") %>
|
|
26
|
+
</span>
|
|
27
|
+
</td>
|
|
28
|
+
</tr>
|
|
29
|
+
<tr>
|
|
30
|
+
<th><%= t('rails_image_post_solution.admin.show.reporter') %></th>
|
|
31
|
+
<td><%= link_to @report.user.display_name, user_path(@report.user), class: "user-link" %></td>
|
|
32
|
+
</tr>
|
|
33
|
+
<tr>
|
|
34
|
+
<th><%= t('rails_image_post_solution.admin.show.reason') %></th>
|
|
35
|
+
<td><%= simple_format(@report.reason) || t('rails_image_post_solution.admin.show.no_reason') %></td>
|
|
36
|
+
</tr>
|
|
37
|
+
<tr>
|
|
38
|
+
<th><%= t('rails_image_post_solution.admin.show.reported_at') %></th>
|
|
39
|
+
<td><%= l(@report.created_at, format: :long) %></td>
|
|
40
|
+
</tr>
|
|
41
|
+
<% if @report.reviewed_by %>
|
|
42
|
+
<tr>
|
|
43
|
+
<th><%= t('rails_image_post_solution.admin.show.reviewer') %></th>
|
|
44
|
+
<td><%= @report.reviewed_by.display_name %></td>
|
|
45
|
+
</tr>
|
|
46
|
+
<tr>
|
|
47
|
+
<th><%= t('rails_image_post_solution.admin.show.reviewed_at') %></th>
|
|
48
|
+
<td><%= l(@report.reviewed_at, format: :long) %></td>
|
|
49
|
+
</tr>
|
|
50
|
+
<% end %>
|
|
51
|
+
</table>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<!-- Image Poster Information -->
|
|
55
|
+
<% if @reported_user %>
|
|
56
|
+
<div class="detail-section">
|
|
57
|
+
<h3><%= t('rails_image_post_solution.admin.show.image_poster') %></h3>
|
|
58
|
+
<table class="detail-table">
|
|
59
|
+
<tr>
|
|
60
|
+
<th><%= t('rails_image_post_solution.admin.show.username') %></th>
|
|
61
|
+
<td><%= link_to @reported_user.display_name, user_path(@reported_user), class: "user-link" %></td>
|
|
62
|
+
</tr>
|
|
63
|
+
<tr>
|
|
64
|
+
<th><%= t('rails_image_post_solution.admin.show.user_status') %></th>
|
|
65
|
+
<td>
|
|
66
|
+
<% if @reported_user.banned? %>
|
|
67
|
+
<span class="user-status-badge banned"><%= t('rails_image_post_solution.admin.show.banned') %></span>
|
|
68
|
+
<% elsif @reported_user.suspended? %>
|
|
69
|
+
<span class="user-status-badge suspended"><%= t('rails_image_post_solution.admin.show.suspended') %></span>
|
|
70
|
+
<% else %>
|
|
71
|
+
<span class="user-status-badge active"><%= t('rails_image_post_solution.admin.show.active') %></span>
|
|
72
|
+
<% end %>
|
|
73
|
+
</td>
|
|
74
|
+
</tr>
|
|
75
|
+
</table>
|
|
76
|
+
</div>
|
|
77
|
+
<% end %>
|
|
78
|
+
|
|
79
|
+
<!-- Other Reports for Same Image -->
|
|
80
|
+
<div class="detail-section">
|
|
81
|
+
<h3><%= t('rails_image_post_solution.admin.show.all_reports_title', count: @all_reports.count) %></h3>
|
|
82
|
+
<% if @all_reports.count > 1 %>
|
|
83
|
+
<table class="admin-table">
|
|
84
|
+
<thead>
|
|
85
|
+
<tr>
|
|
86
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reporter') %></th>
|
|
87
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reason') %></th>
|
|
88
|
+
<th><%= t('rails_image_post_solution.admin.index.table.status') %></th>
|
|
89
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reported_at') %></th>
|
|
90
|
+
</tr>
|
|
91
|
+
</thead>
|
|
92
|
+
<tbody>
|
|
93
|
+
<% @all_reports.each do |r| %>
|
|
94
|
+
<tr class="<%= 'current-report' if r.id == @report.id %>">
|
|
95
|
+
<td>
|
|
96
|
+
<%= link_to r.user.display_name, user_path(r.user), class: "user-link" %>
|
|
97
|
+
<% if r.id == @report.id %>
|
|
98
|
+
<span class="badge-current"><%= t('rails_image_post_solution.admin.show.current') %></span>
|
|
99
|
+
<% end %>
|
|
100
|
+
</td>
|
|
101
|
+
<td><%= truncate(r.reason, length: 50) || t('rails_image_post_solution.admin.index.no_reason') %></td>
|
|
102
|
+
<td>
|
|
103
|
+
<span class="status-badge status-<%= r.status %>">
|
|
104
|
+
<%= t("rails_image_post_solution.image_report.statuses.#{r.status}") %>
|
|
105
|
+
</span>
|
|
106
|
+
</td>
|
|
107
|
+
<td><%= l(r.created_at, format: :long) %></td>
|
|
108
|
+
</tr>
|
|
109
|
+
<% end %>
|
|
110
|
+
</tbody>
|
|
111
|
+
</table>
|
|
112
|
+
<% else %>
|
|
113
|
+
<p class="text-muted"><%= t('rails_image_post_solution.admin.show.single_report') %></p>
|
|
114
|
+
<% end %>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<!-- Actions -->
|
|
118
|
+
<div class="detail-actions">
|
|
119
|
+
<% if @report.status == ImageReport::STATUSES[:pending] %>
|
|
120
|
+
<%= button_to t('rails_image_post_solution.admin.show.confirm_button'), confirm_admin_image_report_path(@report), method: :patch, class: "btn btn-danger", data: { turbo_confirm: t('rails_image_post_solution.admin.show.confirm_action') } %>
|
|
121
|
+
<%= button_to t('rails_image_post_solution.admin.show.dismiss_button'), dismiss_admin_image_report_path(@report), method: :patch, class: "btn btn-success", data: { turbo_confirm: t('rails_image_post_solution.admin.show.dismiss_action') } %>
|
|
122
|
+
<% else %>
|
|
123
|
+
<p class="text-muted"><%= t('rails_image_post_solution.admin.show.already_reviewed') %></p>
|
|
124
|
+
<% end %>
|
|
125
|
+
|
|
126
|
+
<% if @reported_user && @reported_user.active? %>
|
|
127
|
+
<div class="user-actions">
|
|
128
|
+
<h4><%= t('rails_image_post_solution.admin.show.user_actions') %></h4>
|
|
129
|
+
<%= link_to t('rails_image_post_solution.admin.show.view_user_admin'), admin_user_path(@reported_user), class: "btn btn-secondary" %>
|
|
130
|
+
</div>
|
|
131
|
+
<% end %>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="admin-actions">
|
|
136
|
+
<%= link_to t('rails_image_post_solution.admin.show.back_to_list'), admin_image_reports_path, class: "btn btn-default" %>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<div class="admin-container">
|
|
2
|
+
<h1 class="admin-header"><%= t('admin.users.index.title') %></h1>
|
|
3
|
+
|
|
4
|
+
<!-- Statistics -->
|
|
5
|
+
<div class="admin-stats">
|
|
6
|
+
<div class="stat-card">
|
|
7
|
+
<div class="stat-label"><%= t('admin.users.index.stats.total') %></div>
|
|
8
|
+
<div class="stat-value"><%= @stats[:total] %></div>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="stat-card active">
|
|
11
|
+
<div class="stat-label"><%= t('admin.users.index.stats.active') %></div>
|
|
12
|
+
<div class="stat-value"><%= @stats[:active] %></div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="stat-card suspended">
|
|
15
|
+
<div class="stat-label"><%= t('admin.users.index.stats.suspended') %></div>
|
|
16
|
+
<div class="stat-value"><%= @stats[:suspended] %></div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="stat-card banned">
|
|
19
|
+
<div class="stat-label"><%= t('admin.users.index.stats.banned') %></div>
|
|
20
|
+
<div class="stat-value"><%= @stats[:banned] %></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="stat-card admin">
|
|
23
|
+
<div class="stat-label"><%= t('admin.users.index.stats.admin') %></div>
|
|
24
|
+
<div class="stat-value"><%= @stats[:admin] %></div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Filters -->
|
|
29
|
+
<div class="admin-filters">
|
|
30
|
+
<%= link_to t('admin.users.index.filters.all'), admin_users_path, class: "filter-btn #{'active' if @status_filter == 'all'}" %>
|
|
31
|
+
<%= link_to t('admin.users.index.filters.active'), admin_users_path(status: 'active'), class: "filter-btn #{'active' if @status_filter == 'active'}" %>
|
|
32
|
+
<%= link_to t('admin.users.index.filters.suspended'), admin_users_path(status: 'suspended'), class: "filter-btn #{'active' if @status_filter == 'suspended'}" %>
|
|
33
|
+
<%= link_to t('admin.users.index.filters.banned'), admin_users_path(status: 'banned'), class: "filter-btn #{'active' if @status_filter == 'banned'}" %>
|
|
34
|
+
<%= link_to t('admin.users.index.filters.admin'), admin_users_path(status: 'admin'), class: "filter-btn #{'active' if @status_filter == 'admin'}" %>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- User List -->
|
|
38
|
+
<div class="admin-users-list">
|
|
39
|
+
<% if @users.any? %>
|
|
40
|
+
<table class="admin-table">
|
|
41
|
+
<thead>
|
|
42
|
+
<tr>
|
|
43
|
+
<th><%= t('admin.users.index.table.username') %></th>
|
|
44
|
+
<th><%= t('admin.users.index.table.email') %></th>
|
|
45
|
+
<th><%= t('admin.users.index.table.status') %></th>
|
|
46
|
+
<th><%= t('admin.users.index.table.role') %></th>
|
|
47
|
+
<th><%= t('admin.users.index.table.registered_at') %></th>
|
|
48
|
+
<th><%= t('admin.users.index.table.post_count') %></th>
|
|
49
|
+
<th><%= t('admin.users.index.table.actions') %></th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<tbody>
|
|
53
|
+
<% @users.each do |user| %>
|
|
54
|
+
<tr class="user-row">
|
|
55
|
+
<td><%= link_to user.display_name, user_path(user), class: "user-link" %></td>
|
|
56
|
+
<td><%= user.email.present? ? user.email : "-" %></td>
|
|
57
|
+
<td>
|
|
58
|
+
<% if user.banned? %>
|
|
59
|
+
<span class="user-status-badge banned"><%= t('admin.users.index.status_badges.banned') %></span>
|
|
60
|
+
<% elsif user.suspended? %>
|
|
61
|
+
<span class="user-status-badge suspended"><%= t('admin.users.index.status_badges.suspended') %></span>
|
|
62
|
+
<% else %>
|
|
63
|
+
<span class="user-status-badge active"><%= t('admin.users.index.status_badges.active') %></span>
|
|
64
|
+
<% end %>
|
|
65
|
+
</td>
|
|
66
|
+
<td>
|
|
67
|
+
<% if user.admin? %>
|
|
68
|
+
<span class="role-badge admin"><%= t('admin.users.index.role_badges.admin') %></span>
|
|
69
|
+
<% else %>
|
|
70
|
+
<span class="role-badge user"><%= t('admin.users.index.role_badges.user') %></span>
|
|
71
|
+
<% end %>
|
|
72
|
+
</td>
|
|
73
|
+
<td><%= l(user.created_at, format: :short) %></td>
|
|
74
|
+
<td><%= user.stages.count %></td>
|
|
75
|
+
<td class="actions-cell">
|
|
76
|
+
<%= link_to t('admin.users.index.detail'), admin_user_path(user), class: "btn-action btn-detail" %>
|
|
77
|
+
</td>
|
|
78
|
+
</tr>
|
|
79
|
+
<% end %>
|
|
80
|
+
</tbody>
|
|
81
|
+
</table>
|
|
82
|
+
<% else %>
|
|
83
|
+
<div class="empty-state">
|
|
84
|
+
<p><%= t('admin.users.index.no_users') %></p>
|
|
85
|
+
</div>
|
|
86
|
+
<% end %>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div class="admin-actions">
|
|
90
|
+
<%= link_to t('admin.users.index.view_reports'), admin_image_reports_path, class: "btn btn-secondary" %>
|
|
91
|
+
<%= link_to t('admin.users.index.back'), root_path, class: "btn btn-default" %>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<div class="admin-container">
|
|
2
|
+
<h1 class="admin-header"><%= t('admin.users.show.title', name: @user.display_name) %></h1>
|
|
3
|
+
|
|
4
|
+
<!-- User Basic Information -->
|
|
5
|
+
<div class="detail-section">
|
|
6
|
+
<h3><%= t('admin.users.show.basic_info') %></h3>
|
|
7
|
+
<table class="detail-table">
|
|
8
|
+
<tr>
|
|
9
|
+
<th><%= t('admin.users.show.username') %></th>
|
|
10
|
+
<td><%= @user.username %></td>
|
|
11
|
+
</tr>
|
|
12
|
+
<tr>
|
|
13
|
+
<th><%= t('admin.users.show.nickname') %></th>
|
|
14
|
+
<td><%= @user.nickname.present? ? @user.nickname : "-" %></td>
|
|
15
|
+
</tr>
|
|
16
|
+
<tr>
|
|
17
|
+
<th><%= t('admin.users.show.email') %></th>
|
|
18
|
+
<td><%= @user.email.present? ? @user.email : "-" %></td>
|
|
19
|
+
</tr>
|
|
20
|
+
<tr>
|
|
21
|
+
<th><%= t('admin.users.show.status') %></th>
|
|
22
|
+
<td>
|
|
23
|
+
<% if @user.banned? %>
|
|
24
|
+
<span class="user-status-badge banned"><%= t('admin.users.index.status_badges.banned') %></span>
|
|
25
|
+
<div class="status-detail">
|
|
26
|
+
<strong><%= t('admin.users.show.ban_date') %>:</strong> <%= l(@user.banned_at, format: :long) %><br>
|
|
27
|
+
<% if @user.ban_reason.present? %>
|
|
28
|
+
<strong><%= t('admin.users.show.ban_reason') %>:</strong> <%= simple_format(@user.ban_reason) %>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
31
|
+
<% elsif @user.suspended? %>
|
|
32
|
+
<span class="user-status-badge suspended"><%= t('admin.users.index.status_badges.suspended') %></span>
|
|
33
|
+
<div class="status-detail">
|
|
34
|
+
<strong><%= t('admin.users.show.suspension_expiry') %>:</strong> <%= l(@user.suspended_at, format: :long) %><br>
|
|
35
|
+
<% if @user.suspension_reason.present? %>
|
|
36
|
+
<strong><%= t('admin.users.show.suspension_reason') %>:</strong> <%= simple_format(@user.suspension_reason) %>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
<% else %>
|
|
40
|
+
<span class="user-status-badge active"><%= t('admin.users.index.status_badges.active') %></span>
|
|
41
|
+
<% end %>
|
|
42
|
+
</td>
|
|
43
|
+
</tr>
|
|
44
|
+
<tr>
|
|
45
|
+
<th><%= t('admin.users.show.role') %></th>
|
|
46
|
+
<td>
|
|
47
|
+
<% if @user.admin? %>
|
|
48
|
+
<span class="role-badge admin"><%= t('admin.users.index.role_badges.admin') %></span>
|
|
49
|
+
<% else %>
|
|
50
|
+
<span class="role-badge user"><%= t('admin.users.show.basic_info') == t('admin.users.index.role_badges.user') ? t('admin.users.index.role_badges.user') : t('admin.users.index.role_badges.user') %></span>
|
|
51
|
+
<% end %>
|
|
52
|
+
</td>
|
|
53
|
+
</tr>
|
|
54
|
+
<tr>
|
|
55
|
+
<th><%= t('admin.users.show.registered_date') %></th>
|
|
56
|
+
<td><%= l(@user.created_at, format: :long) %></td>
|
|
57
|
+
</tr>
|
|
58
|
+
<tr>
|
|
59
|
+
<th><%= t('admin.users.show.language') %></th>
|
|
60
|
+
<td><%= t("languages.#{@user.locale}") %></td>
|
|
61
|
+
</tr>
|
|
62
|
+
</table>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Activity Statistics -->
|
|
66
|
+
<div class="detail-section">
|
|
67
|
+
<h3><%= t('admin.users.show.activity_stats') %></h3>
|
|
68
|
+
<table class="detail-table">
|
|
69
|
+
<tr>
|
|
70
|
+
<th><%= t('admin.users.show.stage_count') %></th>
|
|
71
|
+
<td><%= @user.stages.count %><%= t('admin.users.show.count_suffix') %></td>
|
|
72
|
+
</tr>
|
|
73
|
+
<tr>
|
|
74
|
+
<th><%= t('admin.users.show.comment_count') %></th>
|
|
75
|
+
<td><%= @user.multiplay_recruitment_comments.count %><%= t('admin.users.show.count_suffix') %></td>
|
|
76
|
+
</tr>
|
|
77
|
+
<tr>
|
|
78
|
+
<th><%= t('admin.users.show.reports_made_count') %></th>
|
|
79
|
+
<td><%= @reports_made.count %><%= t('admin.users.show.count_suffix') %></td>
|
|
80
|
+
</tr>
|
|
81
|
+
<tr>
|
|
82
|
+
<th><%= t('admin.users.show.reports_received_count') %></th>
|
|
83
|
+
<td><%= @reports_received.count %><%= t('admin.users.show.count_suffix') %></td>
|
|
84
|
+
</tr>
|
|
85
|
+
</table>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<!-- Recent Stage Posts -->
|
|
89
|
+
<div class="detail-section">
|
|
90
|
+
<h3><%= t('admin.users.show.recent_stages') %></h3>
|
|
91
|
+
<% if @stages.any? %>
|
|
92
|
+
<ul class="activity-list">
|
|
93
|
+
<% @stages.each do |stage| %>
|
|
94
|
+
<li>
|
|
95
|
+
<%= link_to stage.title, stage_path(stage), class: "activity-link" %>
|
|
96
|
+
<span class="activity-date"><%= l(stage.created_at, format: :short) %></span>
|
|
97
|
+
</li>
|
|
98
|
+
<% end %>
|
|
99
|
+
</ul>
|
|
100
|
+
<% else %>
|
|
101
|
+
<p class="text-muted"><%= t('admin.users.show.no_posts') %></p>
|
|
102
|
+
<% end %>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<!-- Recent Comments -->
|
|
106
|
+
<div class="detail-section">
|
|
107
|
+
<h3><%= t('admin.users.show.recent_comments') %></h3>
|
|
108
|
+
<% if @comments.any? %>
|
|
109
|
+
<ul class="activity-list">
|
|
110
|
+
<% @comments.each do |comment| %>
|
|
111
|
+
<li>
|
|
112
|
+
<%= link_to truncate(comment.content, length: 50), multiplay_recruitment_path(comment.multiplay_recruitment), class: "activity-link" %>
|
|
113
|
+
<span class="activity-date"><%= l(comment.created_at, format: :short) %></span>
|
|
114
|
+
</li>
|
|
115
|
+
<% end %>
|
|
116
|
+
</ul>
|
|
117
|
+
<% else %>
|
|
118
|
+
<p class="text-muted"><%= t('admin.users.show.no_comments') %></p>
|
|
119
|
+
<% end %>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<!-- Report History -->
|
|
123
|
+
<div class="detail-section">
|
|
124
|
+
<h3><%= t('admin.users.show.report_history') %></h3>
|
|
125
|
+
<% if @reports_received.any? %>
|
|
126
|
+
<table class="admin-table">
|
|
127
|
+
<thead>
|
|
128
|
+
<tr>
|
|
129
|
+
<th><%= t('admin.users.show.reporter') %></th>
|
|
130
|
+
<th><%= t('admin.users.show.reason') %></th>
|
|
131
|
+
<th><%= t('admin.users.show.report_status') %></th>
|
|
132
|
+
<th><%= t('admin.users.show.date') %></th>
|
|
133
|
+
</tr>
|
|
134
|
+
</thead>
|
|
135
|
+
<tbody>
|
|
136
|
+
<% @reports_received.each do |report| %>
|
|
137
|
+
<tr>
|
|
138
|
+
<td><%= link_to report.user.display_name, user_path(report.user), class: "user-link" %></td>
|
|
139
|
+
<td><%= truncate(report.reason, length: 50) || t('rails_image_post_solution.admin.index.no_reason') %></td>
|
|
140
|
+
<td>
|
|
141
|
+
<span class="status-badge status-<%= report.status %>">
|
|
142
|
+
<%= t("rails_image_post_solution.image_report.statuses.#{report.status}") %>
|
|
143
|
+
</span>
|
|
144
|
+
</td>
|
|
145
|
+
<td><%= l(report.created_at, format: :short) %></td>
|
|
146
|
+
</tr>
|
|
147
|
+
<% end %>
|
|
148
|
+
</tbody>
|
|
149
|
+
</table>
|
|
150
|
+
<% else %>
|
|
151
|
+
<p class="text-muted"><%= t('admin.users.show.no_reports') %></p>
|
|
152
|
+
<% end %>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- Admin Actions -->
|
|
156
|
+
<div class="detail-actions">
|
|
157
|
+
<h3><%= t('admin.users.show.admin_actions') %></h3>
|
|
158
|
+
|
|
159
|
+
<% if @user.active? %>
|
|
160
|
+
<div class="action-group">
|
|
161
|
+
<%= form_with url: suspend_admin_user_path(@user), method: :patch, class: "inline-form" do |f| %>
|
|
162
|
+
<div class="form-group-inline">
|
|
163
|
+
<%= f.label :duration, "#{t('admin.users.show.suspend_duration')}:" %>
|
|
164
|
+
<%= f.number_field :duration, value: 7, min: 1, max: 365, class: "form-control-small" %>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="form-group-inline">
|
|
167
|
+
<%= f.label :reason, "#{t('admin.users.show.suspend_reason')}:" %>
|
|
168
|
+
<%= f.text_area :reason, rows: 2, class: "form-control", placeholder: t('admin.users.show.suspend_reason_placeholder') %>
|
|
169
|
+
</div>
|
|
170
|
+
<%= f.submit t('admin.users.show.suspend_button'), class: "btn btn-warning", data: { turbo_confirm: t('admin.users.show.suspend_confirm') } %>
|
|
171
|
+
<% end %>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div class="action-group">
|
|
175
|
+
<%= form_with url: ban_admin_user_path(@user), method: :patch, class: "inline-form" do |f| %>
|
|
176
|
+
<div class="form-group-inline">
|
|
177
|
+
<%= f.label :reason, "#{t('admin.users.show.ban_reason')}:" %>
|
|
178
|
+
<%= f.text_area :reason, rows: 2, class: "form-control", placeholder: t('admin.users.show.ban_reason_placeholder') %>
|
|
179
|
+
</div>
|
|
180
|
+
<%= f.submit t('admin.users.show.ban_button'), class: "btn btn-danger", data: { turbo_confirm: t('admin.users.show.ban_confirm') } %>
|
|
181
|
+
<% end %>
|
|
182
|
+
</div>
|
|
183
|
+
<% elsif @user.suspended? %>
|
|
184
|
+
<div class="action-group">
|
|
185
|
+
<%= button_to t('admin.users.show.unsuspend_button'), unsuspend_admin_user_path(@user), method: :patch, class: "btn btn-success", data: { turbo_confirm: t('admin.users.show.unsuspend_confirm') } %>
|
|
186
|
+
</div>
|
|
187
|
+
<% elsif @user.banned? %>
|
|
188
|
+
<div class="action-group">
|
|
189
|
+
<%= button_to t('admin.users.show.unban_button'), unban_admin_user_path(@user), method: :patch, class: "btn btn-success", data: { turbo_confirm: t('admin.users.show.unban_confirm') } %>
|
|
190
|
+
</div>
|
|
191
|
+
<% end %>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div class="admin-actions">
|
|
195
|
+
<%= link_to t('admin.users.show.back_to_list'), admin_users_path, class: "btn btn-default" %>
|
|
196
|
+
<%= link_to t('admin.users.show.view_public_profile'), user_path(@user), class: "btn btn-secondary" %>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<div class="admin-container">
|
|
2
|
+
<h1 class="admin-header"><%= t('rails_image_post_solution.admin.index.title') %></h1>
|
|
3
|
+
|
|
4
|
+
<!-- Statistics -->
|
|
5
|
+
<div class="admin-stats">
|
|
6
|
+
<div class="stat-card">
|
|
7
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.total') %></div>
|
|
8
|
+
<div class="stat-value"><%= @stats[:total] %></div>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="stat-card pending">
|
|
11
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.pending') %></div>
|
|
12
|
+
<div class="stat-value"><%= @stats[:pending] %></div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="stat-card confirmed">
|
|
15
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.confirmed') %></div>
|
|
16
|
+
<div class="stat-value"><%= @stats[:confirmed] %></div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="stat-card dismissed">
|
|
19
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.dismissed') %></div>
|
|
20
|
+
<div class="stat-value"><%= @stats[:dismissed] %></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="stat-card reviewed">
|
|
23
|
+
<div class="stat-label"><%= t('rails_image_post_solution.admin.index.stats.reviewed') %></div>
|
|
24
|
+
<div class="stat-value"><%= @stats[:reviewed] %></div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Filters -->
|
|
29
|
+
<div class="admin-filters">
|
|
30
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.all'), rails_image_post_solution.admin_image_reports_path, class: "filter-btn #{'active' if @status_filter == 'all'}" %>
|
|
31
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.pending'), rails_image_post_solution.admin_image_reports_path(status: 'pending'), class: "filter-btn #{'active' if @status_filter == 'pending'}" %>
|
|
32
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.confirmed'), rails_image_post_solution.admin_image_reports_path(status: 'confirmed'), class: "filter-btn #{'active' if @status_filter == 'confirmed'}" %>
|
|
33
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.dismissed'), rails_image_post_solution.admin_image_reports_path(status: 'dismissed'), class: "filter-btn #{'active' if @status_filter == 'dismissed'}" %>
|
|
34
|
+
<%= link_to t('rails_image_post_solution.admin.index.filters.reviewed'), rails_image_post_solution.admin_image_reports_path(status: 'reviewed'), class: "filter-btn #{'active' if @status_filter == 'reviewed'}" %>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Reports List -->
|
|
38
|
+
<div class="admin-reports-list">
|
|
39
|
+
<% if @reports.any? %>
|
|
40
|
+
<table class="admin-table">
|
|
41
|
+
<thead>
|
|
42
|
+
<tr>
|
|
43
|
+
<th><%= t('rails_image_post_solution.admin.index.table.image') %></th>
|
|
44
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reporter') %></th>
|
|
45
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reason') %></th>
|
|
46
|
+
<th><%= t('rails_image_post_solution.admin.index.table.status') %></th>
|
|
47
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reported_at') %></th>
|
|
48
|
+
<th><%= t('rails_image_post_solution.admin.index.table.reviewer') %></th>
|
|
49
|
+
<th><%= t('rails_image_post_solution.admin.index.table.actions') %></th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<tbody>
|
|
53
|
+
<% @reports.each do |report| %>
|
|
54
|
+
<tr class="report-row status-<%= report.status %>">
|
|
55
|
+
<td class="image-cell">
|
|
56
|
+
<% if report.active_storage_attachment.present? %>
|
|
57
|
+
<%= image_tag report.active_storage_attachment.blob, class: "admin-thumbnail", style: "max-width: 100px; max-height: 100px;" %>
|
|
58
|
+
<% else %>
|
|
59
|
+
<span class="text-muted"><%= t('rails_image_post_solution.admin.index.deleted') %></span>
|
|
60
|
+
<% end %>
|
|
61
|
+
</td>
|
|
62
|
+
<td>
|
|
63
|
+
<% if report.user %>
|
|
64
|
+
<%= report.user.try(:display_name) || report.user.try(:name) || "Unknown" %>
|
|
65
|
+
<% else %>
|
|
66
|
+
<span class="text-muted"><%= t('rails_image_post_solution.admin.index.system') %></span>
|
|
67
|
+
<% end %>
|
|
68
|
+
</td>
|
|
69
|
+
<td class="reason-cell"><%= truncate(report.reason, length: 50) || t('rails_image_post_solution.admin.index.no_reason') %></td>
|
|
70
|
+
<td>
|
|
71
|
+
<span class="status-badge status-<%= report.status %>">
|
|
72
|
+
<%= t("rails_image_post_solution.image_report.statuses.#{report.status}") %>
|
|
73
|
+
</span>
|
|
74
|
+
</td>
|
|
75
|
+
<td><%= l(report.created_at, format: :long) %></td>
|
|
76
|
+
<td>
|
|
77
|
+
<% if report.reviewed_by %>
|
|
78
|
+
<%= report.reviewed_by.try(:display_name) || report.reviewed_by.try(:name) || "Unknown" %>
|
|
79
|
+
<% else %>
|
|
80
|
+
<span class="text-muted">-</span>
|
|
81
|
+
<% end %>
|
|
82
|
+
</td>
|
|
83
|
+
<td class="actions-cell">
|
|
84
|
+
<%= link_to t('rails_image_post_solution.admin.index.detail'), rails_image_post_solution.admin_image_report_path(report), class: "btn-action btn-detail" %>
|
|
85
|
+
</td>
|
|
86
|
+
</tr>
|
|
87
|
+
<% end %>
|
|
88
|
+
</tbody>
|
|
89
|
+
</table>
|
|
90
|
+
<% else %>
|
|
91
|
+
<div class="empty-state">
|
|
92
|
+
<p><%= t('rails_image_post_solution.admin.index.no_reports') %></p>
|
|
93
|
+
</div>
|
|
94
|
+
<% end %>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="admin-actions">
|
|
98
|
+
<%= link_to t('rails_image_post_solution.admin.index.back'), main_app.root_path, class: "btn btn-secondary" %>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|