Codeminer42/Punchclock

View on GitHub
app/admin/user.rb

Summary

Maintainability
B
4 hrs
Test Coverage
B
89%
# frozen_string_literal: true

ActiveAdmin.register User do
  decorate_with UserDecorator

  includes :mentor

  config.sort_order = 'name_asc'

  menu parent: User.model_name.human(count: 2), priority: 1

  permit_params :name, :email, :github, :backend_level, :frontend_level, :contract_type, :contract_company_country, :mentor_id,
                :city_id, :active, :allow_overtime, :office_id, :occupation, :started_at,
                :observation, :specialty, :otp_required_for_login, roles: []

  scope :all
  scope :active, default: true, group: :active
  scope :inactive, group: :active
  scope :office_heads
  scope :admin
  scope :not_allocated, group: :allocation
  scope :allocated, group: :allocation

  filter :name
  filter :email

  filter :backend_level, as: :select, collection: User.backend_level.values.map { |level| [level.text, level.value] }
  filter :frontend_level, as: :select, collection: User.frontend_level.values.map { |level| [level.text, level.value] }

  filter :office, collection: -> { Office.active.order(:city) }
  filter :contract_type, as: :select, collection: User.contract_type.values.map { |contract_type| [contract_type.text.humanize, contract_type.value] }

  filter :by_skills, as: :check_boxes, collection: proc {
    Skill.order(:title).map do |skill|
      [skill.title, skill.id, checked: params.dig(:q, :by_skills_in)&.include?(skill.id.to_s)]
    end
  }

  batch_action :destroy, false
  batch_action :disable, if: proc { params[:scope] != "inactive" } do |ids|
    batch_action_collection.find(ids).each(&:disable!)

    redirect_to collection_path, notice: "The users have been disabled."
  end

  batch_action :enable, if: proc { params[:scope] == "inactive" }  do |ids|
    batch_action_collection.find(ids).each(&:enable!)

    redirect_to collection_path, notice: "The users have been enabled."
  end

  action_item :hour_report_current, only: :index, priority: 0 do
   link_to I18n.t('hour_report_current_month'), hour_report_admin_users_path(format: :xlsx, month: :current)
  end

  action_item :hour_report_past, only: :index, priority: 0 do
   link_to I18n.t('hour_report_past_month'), hour_report_admin_users_path(format: :xlsx, month: :past)
  end

  action_item :user_registration, only: :show, priority: 0, if: -> { !user.confirmed? } do
    link_to I18n.t('resend_user_registration'), resend_user_registration_admin_user_path, method: :patch
  end

  index download_links: [:xlsx] do
    selectable_column
    column :name do |user|
      link_to user.name, admin_user_path(user)
    end
    column :office
    column :backend_level, &:backend_level_text
    column :frontend_level, &:frontend_level_text
    column :allow_overtime
    column :active
    column :"2fa" do |user|
      status_tag user.otp_required_for_login
    end
    actions
  end

  sidebar 'Filtro', only: :show, class: 'hide_custom_filter', partial: 'custom-filter-punches'

  show do
    tabs do
      tab User.model_name.human do
        attributes_table do
          row :name
          row :email
          row :"2fa" do
            status_tag user.otp_required_for_login
          end
          row :github
          row :office
          row :city, &:city_text
          row :managed_offices
          row :english_level
          row :overall_score
          row :performance_score
          row :occupation, &:occupation_text
          row :backend_level, &:backend_level_text
          row :frontend_level, &:frontend_level_text
          row :specialty, &:specialty_text
          row :contract_type, &:contract_type_text
          row :contract_company_country, &:contract_company_country_text

          row :roles do |user|
            user.roles.map { |role| I18n.t(role, scope: 'enumerize.user.role') }
          end

          row :skills do |user|
            raw(skills_tags(user))
          end

          row :mentor
          row :mentees
          row :allow_overtime
          row :active
          row :started_at
          row :last_sign_in_at
          row :created_at
          row :updated_at
          row :observation
        end
      end

      tab Allocation.model_name.human(count: 2) do
        attributes_table do
          row :current_allocation
          row :allocations do
            table_for user.allocations.order(start_at: :desc), i18n: Allocation do
              column :project_name do |allocation|
                allocation.project.name
              end
              column :hourly_rate do |allocation|
                allocation.hourly_rate
              end
              column :start_at
              column :end_at
              column :ongoing
              column '' do |allocation|
                link_to 'Access Allocation', admin_allocation_path(allocation)
              end
            end
          end
        end
      end

      tab I18n.t('performance_evaluations') do
        attributes_table do
          row :evaluation do
            table_for user.evaluations.by_kind(:performance).order(created_at: :desc) do
              column :created_at
              column :evaluator
              column :score
              column :questionnaire
              column '' do |evaluation|
                link_to 'Access Evaluation', admin_evaluation_path(evaluation)
              end
            end
          end
        end
      end

      tab I18n.t('english_evaluations') do
        attributes_table do
          row :english_level
          row :english_score
          row :evaluation do
            table_for user.evaluations.by_kind(:english).order(created_at: :desc) do
              column :created_at
              column :evaluator
              column :score
              column :questionnaire
              column '' do |evaluation|
                link_to 'Access Evaluation', admin_evaluation_path(evaluation)
              end
            end
          end
        end
      end

      tab :punches do
        from = params.dig(:punch, :from_gteq) || 60.days.ago
        to = params.dig(:punch, :from_lteq) || Time.zone.now

        table_for user.punches.where(from: from..to).order(from: :desc).decorate, i18n: Punch, id: 'table_admin_punches' do
          column :project
          column :when
          column :from
          column :to
          column :delta
          column :extra_hour
        end
        div link_to I18n.t('download_as_xls'),
                        admin_punches_path(q: { user_id_eq: user.id, from_greater_than: from, from_lteq: to }, format: :xls)
        div link_to I18n.t('all_punches'),
                        admin_punches_path(q: { user_id_eq: user.id, commit: :Filter })
      end

      tab I18n.t('experience') do
        panel I18n.t('professional_experience') do
          table_for user.professional_experiences.ordered_by_start_date, i18n: ProfessionalExperience do
            column :company
            column :position
            column :description
            column :responsibilities
            column :start_date
            column :end_date
          end
          span do
            link_to I18n.t('active_admin.new_model', model: ProfessionalExperience.model_name.human),
                    new_admin_professional_experience_path(user_id: user),
                    class: "button"
          end
        end

        panel I18n.t('educational_experience') do
          table_for user.education_experiences.decorate, i18n: EducationExperience do
            column :course
            column :institution
            column :start_date
            column :end_date
          end
          span do
            link_to I18n.t('active_admin.new_model', model: EducationExperience.model_name.human),
                    new_admin_education_experience_path(user_id: user),
                    class: "button"
          end
        end

        panel I18n.t('open_source_experience') do
          table_for user.contributions.approved.order(created_at: :desc).decorate, i18n: Contribution do
            column :name, i18n: Repository do |contribution|
              contribution.repository_name
            end
            column :description
            column :created_at
          end
        end

        panel I18n.t('talking_presenting_experience') do
          table_for user.talks.decorate, i18n: Talk do
            column :event_name
            column :talk_title
            column :date
          end

          span do
            link_to I18n.t('active_admin.new_model', model: Talk.model_name.human),
              new_admin_talk_path(user_id: user),
              class: "button"
          end
        end

        span do
          link_to 'Export Experiences (.docx)', export_experience_admin_user_path(user), method: :post
        end
      end
    end
  end

  form do |f|
    f.inputs do
      f.input :name
      f.input :email
      f.input :github
      f.input :started_at, as: :date_picker, input_html: { value: f.object.started_at }
      f.input :city, as: :select, collection: City.collection.map { |city| ["#{city.name} - #{city.state.code}", city.id] }
      f.input :office, collection: Office.active.order(:city)
      f.input :roles, as: :select, multiple: true, collection: User.roles.values.map { |role| [I18n.t(role, scope: 'enumerize.user.role'), role] }
      f.input :mentor, collection: User.active.order(:name)
      f.input :occupation, as: :radio
      f.input :backend_level, as: :select, collection: User.backend_level.options
      f.input :frontend_level, as: :select, collection: User.frontend_level.options
      f.input :specialty, as: :select, collection: User.specialty.values.map { |specialty| [specialty.text.humanize, specialty] }
      f.input :contract_type, as: :select, collection: User.contract_type.values.map { |contract_type| [contract_type.text.humanize, contract_type] }
      f.input :contract_company_country, as: :select, collection: User.contract_company_country.values.map { |company_country| [company_country.text.humanize, company_country] }
      f.input :allow_overtime
      f.input :active
      f.input :otp_required_for_login, as: :boolean, :input_html => { checked: f.object.otp_required_for_login? }
      f.input :observation
    end
    f.actions
  end

  controller do
    def index
      super do |format|
        format.xlsx do
          spreadsheet = UsersSpreadsheet.new find_collection(except: :pagination)
          send_data spreadsheet.to_string_io, filename: 'users.xlsx'
        end
      end
    end

    def save_resource(object)
      object.password_required = false
      object.github = nil if object.github == ''

      super
    end

    def create
      create! do |success, _failure|
        success.html do
          NotificationMailer.notify_user_registration(@user).deliver_later
          redirect_to resource_path
        end
      end
    end
  end

  collection_action :hour_report do
    respond_to do |format|
      reference_date = if params[:month] == "current"
        Date.current
      else
        1.month.ago
      end

      date_range = reference_date.beginning_of_month..reference_date.end_of_month

      reports = find_collection(except: :pagination).flat_map do |user|
        HourReport.build_reports_for(user, date_range)
      end

      spreadsheet = HourReportSpreadsheet.new reports

      format.xlsx { send_data spreadsheet.to_string_io, filename: "users-hours-#{Date.current}.xlsx" }
    end
  end

  member_action :resend_user_registration, method: :patch do
    user = resource.user

    user.transaction do
      user.touch(:confirmed_at)
      token = user.send(:set_reset_password_token)
      NotificationMailer.resend_user_registration(resource, token).deliver_later

      redirect_to admin_user_path, notice: I18n.t('resend_user_registration_confirmed')
    end
  end

  member_action :export_experience, method: :post do
    user = User.find(params[:id])
    doc = UserResumeDoc.new.call(user)

    send_data doc.to_string_io, filename: doc.filename
  end
end