beetle_reporter 0.1.0 → 0.1.1
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/MIT-LICENSE +20 -0
- data/README.md +70 -16
- data/Rakefile +5 -9
- data/app/assets/config/beetle_reporter_manifest.js +1 -0
- data/app/assets/stylesheets/beetle_reporter/application.css +15 -0
- data/app/assets/stylesheets/beetle_reporter/home.css +4 -0
- data/app/assets/stylesheets/beetle_reporter/reports.css +4 -0
- data/app/assets/stylesheets/beetle_reporter/tailwind.min.css +1 -0
- data/app/assets/stylesheets/scaffold.css +80 -0
- data/app/controllers/beetle_reporter/application_controller.rb +11 -0
- data/app/controllers/beetle_reporter/home_controller.rb +8 -0
- data/app/controllers/beetle_reporter/reports_controller.rb +143 -0
- data/app/helpers/beetle_reporter/application_helper.rb +9 -0
- data/app/helpers/beetle_reporter/home_helper.rb +4 -0
- data/app/helpers/beetle_reporter/reports_helper.rb +4 -0
- data/app/jobs/beetle_reporter/application_job.rb +4 -0
- data/app/mailers/beetle_reporter/application_mailer.rb +6 -0
- data/app/models/beetle_reporter/application_record.rb +5 -0
- data/app/models/beetle_reporter/report.rb +126 -0
- data/app/views/beetle_reporter/home/index.html.haml +4 -0
- data/app/views/beetle_reporter/reports/_form.html.haml +14 -0
- data/app/views/beetle_reporter/reports/_report.html.haml +57 -0
- data/app/views/beetle_reporter/reports/destroy.turbo_stream.haml +1 -0
- data/app/views/beetle_reporter/reports/edit.html.haml +6 -0
- data/app/views/beetle_reporter/reports/index.html.haml +38 -0
- data/app/views/beetle_reporter/reports/new.html.haml +5 -0
- data/app/views/beetle_reporter/reports/show.html.haml +1 -0
- data/app/views/layouts/beetle_reporter/application.html.haml +22 -0
- data/config/locales/beetle_reporter.yml +18 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20211216073022_create_beetle_reporter_reports.rb +20 -0
- data/lib/beetle_reporter/configuration.rb +9 -0
- data/lib/beetle_reporter/engine.rb +9 -0
- data/lib/beetle_reporter/version.rb +1 -3
- data/lib/beetle_reporter.rb +46 -4
- data/lib/tasks/beetle_reporter_tasks.rake +4 -0
- metadata +137 -15
- data/.rspec +0 -3
- data/.rubocop.yml +0 -13
- data/CHANGELOG.md +0 -5
- data/Gemfile +0 -12
- data/LICENSE.txt +0 -21
- data/beetle_reporter.gemspec +0 -39
- data/bin/console +0 -15
- data/bin/setup +0 -8
| @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            body {
         | 
| 2 | 
            +
              background-color: #fff;
         | 
| 3 | 
            +
              color: #333;
         | 
| 4 | 
            +
              margin: 33px;
         | 
| 5 | 
            +
            }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            body, p, ol, ul, td {
         | 
| 8 | 
            +
              font-family: verdana, arial, helvetica, sans-serif;
         | 
| 9 | 
            +
              font-size: 13px;
         | 
| 10 | 
            +
              line-height: 18px;
         | 
| 11 | 
            +
            }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            pre {
         | 
| 14 | 
            +
              background-color: #eee;
         | 
| 15 | 
            +
              padding: 10px;
         | 
| 16 | 
            +
              font-size: 11px;
         | 
| 17 | 
            +
            }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            a {
         | 
| 20 | 
            +
              color: #000;
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            a:visited {
         | 
| 24 | 
            +
              color: #666;
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            a:hover {
         | 
| 28 | 
            +
              color: #fff;
         | 
| 29 | 
            +
              background-color: #000;
         | 
| 30 | 
            +
            }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            th {
         | 
| 33 | 
            +
              padding-bottom: 5px;
         | 
| 34 | 
            +
            }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            td {
         | 
| 37 | 
            +
              padding: 0 5px 7px;
         | 
| 38 | 
            +
            }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            div.field,
         | 
| 41 | 
            +
            div.actions {
         | 
| 42 | 
            +
              margin-bottom: 10px;
         | 
| 43 | 
            +
            }
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            #notice {
         | 
| 46 | 
            +
              color: green;
         | 
| 47 | 
            +
            }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            .field_with_errors {
         | 
| 50 | 
            +
              padding: 2px;
         | 
| 51 | 
            +
              background-color: red;
         | 
| 52 | 
            +
              display: table;
         | 
| 53 | 
            +
            }
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            #error_explanation {
         | 
| 56 | 
            +
              width: 450px;
         | 
| 57 | 
            +
              border: 2px solid red;
         | 
| 58 | 
            +
              padding: 7px 7px 0;
         | 
| 59 | 
            +
              margin-bottom: 20px;
         | 
| 60 | 
            +
              background-color: #f0f0f0;
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            #error_explanation h2 {
         | 
| 64 | 
            +
              text-align: left;
         | 
| 65 | 
            +
              font-weight: bold;
         | 
| 66 | 
            +
              padding: 5px 5px 5px 15px;
         | 
| 67 | 
            +
              font-size: 12px;
         | 
| 68 | 
            +
              margin: -7px -7px 0;
         | 
| 69 | 
            +
              background-color: #c00;
         | 
| 70 | 
            +
              color: #fff;
         | 
| 71 | 
            +
            }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            #error_explanation ul li {
         | 
| 74 | 
            +
              font-size: 12px;
         | 
| 75 | 
            +
              list-style: square;
         | 
| 76 | 
            +
            }
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            label {
         | 
| 79 | 
            +
              display: block;
         | 
| 80 | 
            +
            }
         | 
| @@ -0,0 +1,143 @@ | |
| 1 | 
            +
            module BeetleReporter
         | 
| 2 | 
            +
              class ReportsController < BeetleReporter::ApplicationController
         | 
| 3 | 
            +
                before_action :load_report, only: %w[show edit update destroy ignore undo suspend hide remove mute forbid]
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def index
         | 
| 6 | 
            +
                  # authorize Report
         | 
| 7 | 
            +
              
         | 
| 8 | 
            +
                  load_reports
         | 
| 9 | 
            +
              
         | 
| 10 | 
            +
                  # skip_policy_scope
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              
         | 
| 13 | 
            +
                def show
         | 
| 14 | 
            +
                  # authorize @report
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              
         | 
| 17 | 
            +
                def new
         | 
| 18 | 
            +
                  # authorize Report
         | 
| 19 | 
            +
              
         | 
| 20 | 
            +
                  @report = Report.new
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              
         | 
| 23 | 
            +
                def create
         | 
| 24 | 
            +
                  # authorize Report
         | 
| 25 | 
            +
              
         | 
| 26 | 
            +
                  create_params = report_params.to_hash.deep_symbolize_keys
         | 
| 27 | 
            +
                  @report = report_scope.new(create_params)
         | 
| 28 | 
            +
                  @report.user = current_user
         | 
| 29 | 
            +
              
         | 
| 30 | 
            +
                  respond_to do |format|
         | 
| 31 | 
            +
                    if @report.save
         | 
| 32 | 
            +
                      format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@report), partial: "report", locals: {report: @report}) }
         | 
| 33 | 
            +
                      format.html { redirect_to reports_path, notice: "report was successfully created." }
         | 
| 34 | 
            +
                    else
         | 
| 35 | 
            +
                      format.html { render :edit, status: :unprocessable_entity }
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              
         | 
| 40 | 
            +
                def edit
         | 
| 41 | 
            +
                  # authorize @report
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              
         | 
| 44 | 
            +
                def update
         | 
| 45 | 
            +
                  # authorize @report
         | 
| 46 | 
            +
              
         | 
| 47 | 
            +
                  update_params = report_params.to_hash.deep_symbolize_keys
         | 
| 48 | 
            +
              
         | 
| 49 | 
            +
                  respond_to do |format|
         | 
| 50 | 
            +
                    if @report.update(update_params)
         | 
| 51 | 
            +
                      format.turbo_stream { render turbo_stream: turbo_stream.replace(dom_id(@report), partial: "report", locals: {report: @report}) }
         | 
| 52 | 
            +
                      format.html { redirect_to reports_path, notice: "report was successfully updated." }
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      format.html { render :edit, status: :unprocessable_entity }
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              
         | 
| 59 | 
            +
                def destroy
         | 
| 60 | 
            +
                  # authorize @report
         | 
| 61 | 
            +
              
         | 
| 62 | 
            +
                  @report.discard
         | 
| 63 | 
            +
              
         | 
| 64 | 
            +
                  respond_to do |format|
         | 
| 65 | 
            +
                    format.turbo_stream
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def ignore
         | 
| 70 | 
            +
                  @report.ignore!
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  redirect_to reports_url, notice: 'report was successfully ignored.'
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def suspend
         | 
| 76 | 
            +
                  @report.suspend!
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  redirect_to reports_url, notice: 'report was successfully ignored.'
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def hide
         | 
| 82 | 
            +
                  @report.beetle_hide("fooobar hide")
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  redirect_to reports_url, notice: 'report was successfully hidden.'
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                def remove
         | 
| 88 | 
            +
                  @report.beetle_remove("fooobar remove")
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  redirect_to reports_url, notice: 'report was successfully removed.'
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def mute
         | 
| 94 | 
            +
                  @report.beetle_mute("fooobar mute")
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  redirect_to reports_url, notice: 'report was successfully muted.'
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def forbid
         | 
| 100 | 
            +
                  @report.beetle_forbid("fooobar forbid")
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  redirect_to reports_url, notice: 'report was successfully forbiden.'
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                def undo
         | 
| 106 | 
            +
                  @report.undo!
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  redirect_to reports_url, notice: 'report was successfully ignored.'
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
              
         | 
| 111 | 
            +
                private
         | 
| 112 | 
            +
              
         | 
| 113 | 
            +
                def load_reports
         | 
| 114 | 
            +
                  builder = report_scope.includes(:record)
         | 
| 115 | 
            +
                  builder = builder.where("reason ilike ?", "%#{params[:q]}%") if params[:q].present?
         | 
| 116 | 
            +
                  if params[:filter] != 'all'
         | 
| 117 | 
            +
                    builder = builder.pending
         | 
| 118 | 
            +
                    builder = builder.unscope(where: :aasm_state).where(aasm_state: params[:state]) if params[:state].present?
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
                  builder = builder.where(record_type: params[:type]&.classify) if params[:type]
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  builder = builder.order(id: :desc, reported_count: :desc)
         | 
| 123 | 
            +
                  builder = builder.reorder(updated_at: :desc) if params[:order] == 'last_updated'
         | 
| 124 | 
            +
                  builder = builder.reorder(created_at: :desc) if params[:order] == 'last_created'
         | 
| 125 | 
            +
                  builder = builder.reorder(updated_at: :asc) if params[:order] == 'oldest_updated'
         | 
| 126 | 
            +
                  builder = builder.reorder(created_at: :asc) if params[:order] == 'oldest_created'
         | 
| 127 | 
            +
              
         | 
| 128 | 
            +
                  @pagy, @reports = pagy(builder)
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
              
         | 
| 131 | 
            +
                def load_report
         | 
| 132 | 
            +
                  @report = report_scope.find(params[:id] || params[:report_id])
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
              
         | 
| 135 | 
            +
                def report_scope
         | 
| 136 | 
            +
                  BeetleReporter::Report.kept
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
              
         | 
| 139 | 
            +
                def report_params
         | 
| 140 | 
            +
                  params.require(:report).permit(:id, :record_id, :record_type, :reason, :user_id, :device_id)
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
            end
         | 
| @@ -0,0 +1,126 @@ | |
| 1 | 
            +
            module BeetleReporter
         | 
| 2 | 
            +
              class Report < ApplicationRecord
         | 
| 3 | 
            +
                include AASM
         | 
| 4 | 
            +
                include Discard::Model
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                belongs_to :record, polymorphic: true, required: false
         | 
| 7 | 
            +
                belongs_to :user, class_name: BeetleReporter.config.user_class, optional: true
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                aasm do
         | 
| 10 | 
            +
                  state :pending, initial: true
         | 
| 11 | 
            +
                  state :ignored, :hidden, :removed, :muted, :forbiden, :suspended
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  event :ignore do
         | 
| 14 | 
            +
                    transitions from: :pending, to: :ignored
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  event :hide do
         | 
| 18 | 
            +
                    transitions from: %i[pending ignored], to: :hidden, after: Proc.new {|*args| set_reason(*args) }
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  event :remove do
         | 
| 22 | 
            +
                    transitions from: %i[pending ignored], to: :removed, after: Proc.new {|*args| set_reason(*args) }
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  event :mute do
         | 
| 26 | 
            +
                    transitions from: %i[pending ignored], to: :muted, after: Proc.new {|*args| set_reason(*args) }
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  event :forbid do
         | 
| 30 | 
            +
                    transitions from: %i[pending muted], to: :forbiden, after: Proc.new {|*args| set_reason(*args) }
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  event :suspend do
         | 
| 34 | 
            +
                    transitions from: :pending, to: :suspended
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  event :undo do
         | 
| 38 | 
            +
                    transitions from: %i[ignored hidden removed muted forbiden suspended], to: :pending, after: :undo_callback
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def beetle_hide(reason)
         | 
| 43 | 
            +
                  return if record.blank?
         | 
| 44 | 
            +
                  raise NoMethodError unless record.respond_to?(:beetle_hide)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  ret = record.beetle_hide
         | 
| 47 | 
            +
                  hide!(reason) if ret
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def beetle_remove(reason)
         | 
| 51 | 
            +
                  return if record.blank?
         | 
| 52 | 
            +
                  raise NoMethodError unless record.respond_to?(:beetle_remove)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  ret = record.beetle_remove
         | 
| 55 | 
            +
                  remove!(reason) if ret
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                def beetle_mute(reason)
         | 
| 59 | 
            +
                  return if record.blank?
         | 
| 60 | 
            +
                  return if record&.user.blank?
         | 
| 61 | 
            +
                  raise NoMethodError unless record.respond_to?(:beetle_mute)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  ret = record.beetle_mute
         | 
| 64 | 
            +
                  mute!(reason) if ret
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def beetle_mute(reason)
         | 
| 68 | 
            +
                  return if record.blank?
         | 
| 69 | 
            +
                  return if record&.user.blank?
         | 
| 70 | 
            +
                  raise NoMethodError unless record.respond_to?(:beetle_mute)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  ret = record.beetle_mute
         | 
| 73 | 
            +
                  mute!(reason) if ret
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                def beetle_forbid(reason)
         | 
| 77 | 
            +
                  return if record.blank?
         | 
| 78 | 
            +
                  return if record&.user.blank?
         | 
| 79 | 
            +
                  raise NoMethodError unless record.respond_to?(:beetle_forbid)
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  ret = record.beetle_forbid
         | 
| 82 | 
            +
                  forbid!(reason) if ret
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def record_path
         | 
| 86 | 
            +
                  return '#' if record.blank?
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  record.beetle_record_path
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def undo_callback
         | 
| 92 | 
            +
                  if hidden?
         | 
| 93 | 
            +
                    ret = record.beetle_undo_hide
         | 
| 94 | 
            +
                    update_columns(reason: nil) if ret
         | 
| 95 | 
            +
                  elsif removed?
         | 
| 96 | 
            +
                    ret = record.beetle_undo_remove
         | 
| 97 | 
            +
                    update_columns(reason: nil) if ret
         | 
| 98 | 
            +
                  elsif muted?
         | 
| 99 | 
            +
                    ret = record.beetle_undo_mute
         | 
| 100 | 
            +
                    update_columns(reason: nil) if ret
         | 
| 101 | 
            +
                  elsif forbiden?
         | 
| 102 | 
            +
                    ret = record.beetle_undo_forbid
         | 
| 103 | 
            +
                    update_columns(reason: nil) if ret
         | 
| 104 | 
            +
                  else
         | 
| 105 | 
            +
                    update_columns(reason: nil)
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def images
         | 
| 110 | 
            +
                  return [] if record.blank?
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  raise NoMethodError, 'undefined beetle_images for record' unless record.respond_to?(:beetle_images)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  record.beetle_images
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
             | 
| 118 | 
            +
                def set_reason(reason)
         | 
| 119 | 
            +
                  update_columns(reason: reason)
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def self.record_type_options
         | 
| 123 | 
            +
                  select(:record_type).where.not(record_type: nil).distinct.map{|t| [t.record_type, t.record_type.downcase]}
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
              end
         | 
| 126 | 
            +
            end
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            - url = report&.id.blank? ? reports_path : report_path(report)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            = form_with(model: report, id: dom_id(report), url: url, local: true ) do |form|
         | 
| 4 | 
            +
              - if report.errors.any?
         | 
| 5 | 
            +
                %h2= "#{pluralize(report.errors.count, "error")} prohibited this report from being saved:"
         | 
| 6 | 
            +
                %ul
         | 
| 7 | 
            +
                  - report.errors.each do |error|
         | 
| 8 | 
            +
                    %li= error.full_message
         | 
| 9 | 
            +
              %div{class: "space-y-2"}
         | 
| 10 | 
            +
                %div
         | 
| 11 | 
            +
                  %div{class: "text-gray-500 text-xs"}= form.label :reason, "举报原因"
         | 
| 12 | 
            +
                  %div= form.text_field :reason, class: "border p-1 w-full rounded-md"
         | 
| 13 | 
            +
                %div
         | 
| 14 | 
            +
                  %div= form.submit "确认", class: "px-2 py-0.5 rounded-md bg-gray-800 text-white"
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            = turbo_frame_tag dom_id(report) do
         | 
| 2 | 
            +
              %div{class: "flex flex-col space-y-2 border rounded-md p-2 mt-2"}
         | 
| 3 | 
            +
                %div{class: "flex justify-between text-xs"}
         | 
| 4 | 
            +
                  %span{class: "space-x-2"}
         | 
| 5 | 
            +
                    - unless report.record.blank?
         | 
| 6 | 
            +
                      = link_to report.record_path, data: { turbo: false }, target: "_blank" do
         | 
| 7 | 
            +
                        %span{class: "bg-indigo-100 text-indigo-600 py-1 px-2 rounded-lg"}="#{report.record_type.upcase}##{report.record_id}"
         | 
| 8 | 
            +
                      %span{class: "bg-yellow-100 text-yellow-600 rounded-md p-1"}=t("beetle_reporter.aasm_state.#{report.aasm_state}")
         | 
| 9 | 
            +
                  %span{class: "flex space-x-2 items-center text-sm"}
         | 
| 10 | 
            +
                    - if report.reason
         | 
| 11 | 
            +
                      %span{class: "bg-red-100 text-red-600 p-1 rounded-lg"}= "处置原因:#{report.reason}"
         | 
| 12 | 
            +
                    %span{class: "space-x-1"}
         | 
| 13 | 
            +
                      - report.user_ids.each do |uid|
         | 
| 14 | 
            +
                        = link_to main_app.send(BeetleReporter.config.main_app_user_path_method.to_sym, uid), data: {turbo: false} do
         | 
| 15 | 
            +
                          %span{class: "bg-blue-100 rounded-lg px-1 py-0.5"}= "#{uid}"
         | 
| 16 | 
            +
                    %span{class: "bg-red-600 text-white rounded-full w-5 h-5 flex justify-center items-center"}= "#{report.reported_count}"
         | 
| 17 | 
            +
                    %span{class: "bg-gray-100 text-gray-600 py-1 px-2 rounded-lg"}
         | 
| 18 | 
            +
                      = link_to report.created_at, report_path(report), data: {turbo: false}
         | 
| 19 | 
            +
                    - unless current_user&.admin?
         | 
| 20 | 
            +
                      = button_to '删除', report, method: :delete, data: { confirm: 'Are you sure?'}, class: "bg-red-600 text-white rounded-md px-2 py-0.5 cursor-pointer"
         | 
| 21 | 
            +
                %div{class: "flex justify-between items-center py-2 w-full"}
         | 
| 22 | 
            +
                  %div{class: "flex flex-col rounded-lg bg-gray-50 p-3 text-sm space-y-4 w-full"}
         | 
| 23 | 
            +
                    -unless report.record
         | 
| 24 | 
            +
                      %h3{class: "text-2xl text-red-600"} 对应条目已被永久删除,当前审核请求已无效,请删除
         | 
| 25 | 
            +
                    - else
         | 
| 26 | 
            +
                      %div
         | 
| 27 | 
            +
                        %span{class: "#{report.record.is_private ? 'bg-black text-white' : 'border border-black text-black' } rounded-lg text-xs p-0.5 px-1"}=report.record.is_private ? "🙈 隐私" : "🙉 公开"
         | 
| 28 | 
            +
                        %span{class: "border border-blue-600 text-blue-600 rounded-lg text-xs p-0.5"}=report.record.is_approved ? "审核通过" : "审核中..."
         | 
| 29 | 
            +
                        - unless report.reasons.blank?
         | 
| 30 | 
            +
                          - report.reasons.each do |reason|
         | 
| 31 | 
            +
                            %span{class: "border border-red-600 text-red-600 rounded-lg text-xs p-0.5"}= reason
         | 
| 32 | 
            +
                        - unless report&.record&.user_id&.blank?
         | 
| 33 | 
            +
                          = link_to main_app.send(CensorBear.config.main_app_user_path_method.to_sym, report.record.user_id), data: {turbo: false} do
         | 
| 34 | 
            +
                            %span{class: "text-sm bg-gray-100 text-gray-600 py-1 px-2 rounded-lg font-bold"}="✍️ ##{report.record.user_id}"
         | 
| 35 | 
            +
                      - if report.record.discarded?
         | 
| 36 | 
            +
                        %div{class: "text-gray-900 leading-relaxed"}
         | 
| 37 | 
            +
                          %del= report.record.content
         | 
| 38 | 
            +
                      - else
         | 
| 39 | 
            +
                        %div{class: "text-gray-900 leading-relaxed"}= report.record.content
         | 
| 40 | 
            +
                      %div
         | 
| 41 | 
            +
                        - report.images.each do |img|
         | 
| 42 | 
            +
                          = image_tag img[:src], class: "w-16 rounded-md"
         | 
| 43 | 
            +
                %div{class: "flex space-x-2 items-center justify-end"}
         | 
| 44 | 
            +
                  - if report.may_ignore?
         | 
| 45 | 
            +
                    = link_to "忽略", report_ignore_path(report), class: "text-yellow-600 cursor-pointer"
         | 
| 46 | 
            +
                  - if report.may_hide?
         | 
| 47 | 
            +
                    = link_to "隐藏内容", report_hide_path(report), class: "text-purple-600 cursor-pointer"
         | 
| 48 | 
            +
                  - if report.may_remove?
         | 
| 49 | 
            +
                    = link_to "删除内容", report_remove_path(report), class: "text-red-800 cursor-pointer"
         | 
| 50 | 
            +
                  - if report.may_mute?
         | 
| 51 | 
            +
                    = link_to "禁言用户", report_mute_path(report), class: "text-red-600 cursor-pointer"
         | 
| 52 | 
            +
                  - if report.may_forbid?
         | 
| 53 | 
            +
                    = link_to "封禁用户",report_forbid_path(report), class: "text-red-700 cursor-pointer"
         | 
| 54 | 
            +
                  - if report.may_suspend?
         | 
| 55 | 
            +
                    = link_to "暂缓处理", report_suspend_path(report), class: "text-indigo-600 cursor-pointer"
         | 
| 56 | 
            +
                  - if report.may_undo?
         | 
| 57 | 
            +
                    = link_to "撤销", report_undo_path(report), class: "text-red-600 cursor-pointer"
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            = turbo_stream.remove(@report)
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            %div{class: "flex flex-col space-y-2 justify-center"}
         | 
| 2 | 
            +
              %div{class: "flex justify-between"}
         | 
| 3 | 
            +
                %div
         | 
| 4 | 
            +
                  = form_with(url: reports_path, method: :get, class: "flex items-center") do |f|
         | 
| 5 | 
            +
                    %div{class: "space-x-0.5"}
         | 
| 6 | 
            +
                      = f.text_field :q, value: params[:q], placeholder: "关键词", class: "border rounded-md m py-0.5 px-1"
         | 
| 7 | 
            +
                      = f.select :type, options_for_select(BeetleReporter::Report.record_type_options, params[:type]), {include_blank: "-- 筛选 --"}, class: "border rounded-md m py-0.5 px-1"
         | 
| 8 | 
            +
                      = f.select :order, options_for_select([%w[按最近创建 last_created], %w[按最晚创建 oldest_created]], params[:order]), {include_blank: "-- 排序 --"}, class: "border rounded-md m py-0.5 px-1"
         | 
| 9 | 
            +
                      = f.hidden_field :state, value: params[:state]
         | 
| 10 | 
            +
                      = f.hidden_field :filter, value: params[:filter]
         | 
| 11 | 
            +
                      = f.submit "检索", class: "rounded-md px-2 py-1 text-sm bg-black text-white cursor-pointer"
         | 
| 12 | 
            +
                      = link_to "重置", reports_path, class: "text-sm"
         | 
| 13 | 
            +
                %div{class: "flex items-center space-x-2"}
         | 
| 14 | 
            +
                  %div{class: "text-sm text-gray-600"}="共 #{@pagy.count} 条"
         | 
| 15 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state].blank? && params[:filter].blank? ? 'font-bold' : ''}"}
         | 
| 16 | 
            +
                    = link_to "待处理", request.params.merge(state: "", filter: "")
         | 
| 17 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'ignored' ? 'font-bold' : ''}"}
         | 
| 18 | 
            +
                    = link_to "无效举报", request.params.merge(state: "ignored", filter: "")
         | 
| 19 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'hidden' ? 'font-bold' : ''}"}
         | 
| 20 | 
            +
                    = link_to "已隐藏", request.params.merge(state: "hidden", filter: "")
         | 
| 21 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'removed' ? 'font-bold' : ''}"}
         | 
| 22 | 
            +
                    = link_to "已删除", request.params.merge(state: "removed", filter: "")
         | 
| 23 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'muted' ? 'font-bold' : ''}"}
         | 
| 24 | 
            +
                    = link_to "已禁言用户", request.params.merge(state: "muted", filter: "")
         | 
| 25 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'forbiden' ? 'font-bold' : ''}"}
         | 
| 26 | 
            +
                    = link_to "已封禁用户", request.params.merge(state: "forbiden", filter: "")
         | 
| 27 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:state] == 'suspended' ? 'font-bold' : ''}"}
         | 
| 28 | 
            +
                    = link_to "暂缓处理", request.params.merge(state: "suspended", filter: "")
         | 
| 29 | 
            +
                  %div{class: "bg-gray-200 rounded-full px-3 py-1 text-xs #{params[:filter] == "all" ? 'font-bold' : ''}"}
         | 
| 30 | 
            +
                    = link_to "全部", request.params.merge(filter: "all", state: "")
         | 
| 31 | 
            +
              %div
         | 
| 32 | 
            +
                - if @reports.blank?
         | 
| 33 | 
            +
                  %div{class: "flex justify-center text-gray-500 p-8 border rounded-md"} 空空如也
         | 
| 34 | 
            +
                - else
         | 
| 35 | 
            +
                  - @reports.each do |report|
         | 
| 36 | 
            +
                    = render report
         | 
| 37 | 
            +
                %div{class: "flex justify-center mt-2"}
         | 
| 38 | 
            +
                  = raw pagy_nav(@pagy)
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            = render @report
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            !!! 5
         | 
| 2 | 
            +
            %html
         | 
| 3 | 
            +
              %head
         | 
| 4 | 
            +
                %title 🐞 举报中心
         | 
| 5 | 
            +
                = csrf_meta_tags
         | 
| 6 | 
            +
                = csp_meta_tag
         | 
| 7 | 
            +
                = stylesheet_link_tag    "beetle_reporter/application", media: "all"
         | 
| 8 | 
            +
                = javascript_include_tag "turbo", type: "module"
         | 
| 9 | 
            +
                = yield :head
         | 
| 10 | 
            +
              %body{class: "flex flex-col space-y-6 p-4"}
         | 
| 11 | 
            +
                %div{class: "flex justify-between items-center"}
         | 
| 12 | 
            +
                  %h2{class: "text-xl bg-black text-white rounded-full py-1 px-4 cursor-pointer"}
         | 
| 13 | 
            +
                    %span{class: "shadow-lg"} 🐞
         | 
| 14 | 
            +
                    %span 举报中心
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  %ul{class: "flex justify-end items-center space-x-2 text-blue-600"}
         | 
| 17 | 
            +
                    %li= link_to "待处理(#{BeetleReporter::Report.pending.count})", reports_path
         | 
| 18 | 
            +
                    %li= link_to "控制台", main_app.send(BeetleReporter.config.main_app_root_path_method.to_sym)
         | 
| 19 | 
            +
                    %li= current_user.email
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                %div
         | 
| 22 | 
            +
                  = yield
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            "zh-CN":
         | 
| 2 | 
            +
              beetle_reporter:
         | 
| 3 | 
            +
                action:
         | 
| 4 | 
            +
                  mod: 审核
         | 
| 5 | 
            +
                  banned: 禁用
         | 
| 6 | 
            +
                  ignore: 忽略
         | 
| 7 | 
            +
                  replace: 替换
         | 
| 8 | 
            +
                  block: 禁用
         | 
| 9 | 
            +
                  pass: 忽略
         | 
| 10 | 
            +
                  review: 审核
         | 
| 11 | 
            +
                aasm_state:
         | 
| 12 | 
            +
                  pending: 待审
         | 
| 13 | 
            +
                  suspended: 暂缓处理
         | 
| 14 | 
            +
                  ignored: 无效举报
         | 
| 15 | 
            +
                  hidden: 已隐藏
         | 
| 16 | 
            +
                  removed: 已删除
         | 
| 17 | 
            +
                  muted: 已禁言用户
         | 
| 18 | 
            +
                  forbiden: 已封禁用户
         | 
    
        data/config/routes.rb
    ADDED
    
    
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            class CreateBeetleReporterReports < ActiveRecord::Migration[6.1]
         | 
| 2 | 
            +
              def change
         | 
| 3 | 
            +
                create_table :beetle_reporter_reports do |t|
         | 
| 4 | 
            +
                  t.bigint :record_id
         | 
| 5 | 
            +
                  t.string :record_type
         | 
| 6 | 
            +
                  t.string :reason
         | 
| 7 | 
            +
                  t.bigint :user_id
         | 
| 8 | 
            +
                  t.bigint :user_ids, array: true, default: []
         | 
| 9 | 
            +
                  t.string :reasons, array: true, default: []
         | 
| 10 | 
            +
                  t.integer :reported_count, default: 0
         | 
| 11 | 
            +
                  t.string :device_id
         | 
| 12 | 
            +
                  t.string :aasm_state
         | 
| 13 | 
            +
                  t.string :treatment_reason
         | 
| 14 | 
            +
                  t.datetime :discarded_at
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  t.timestamps
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                add_index :beetle_reporter_reports, :discarded_at
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         |