Have them enter their name and email address before deleting the user account

reference

Pull Request

Preparation

rails new user_delete_app -d postgresql

bin/rails g scaffold user name:string email:string

bin/rails db:create

bin/rails db:migrate

Slim related addition to Gemifile

gem 'slim-rails'
gem 'html2slim'

Hit the following in the terminal

bundle exec erb2slim app/views app/views -d

[Add Tailwind](https://qiita.com/sakusakupg/items/a5f61818b76c7009f8ff#tailwind%E3%82%92%E5%85%A5%E3%82%8C%E3%81%A6%E3%81% BF% E3% 82% 8B)

seed data preparation

db/seeds.rb


User.create(
  [
    {name: 'Jane Cooper', email: '[email protected]'},
    {name: 'Cody Fisher', email: '[email protected]'},
    {name: 'Esther Howard', email: '[email protected]'},
    {name: 'Jenny Wilson', email: '[email protected]'},
    {name: 'Kristin Watson', email: '[email protected]'},
    {name: 'Cameron Williamson', email: '[email protected]'}
  ]
)

Hit the following in the terminal

bin/rails db:seed

slim:app/views/users/index.html.slim


p#notice
  = notice
.ml-5
  .flex.flex-col.py-5
    .-my-2.overflow-x-auto(class="sm:-mx-6 lg:-mx-8")
    .py-2.align-middle.inline-block.min-w-full(class="sm:px-6 lg:px-8")
      .shadow.overflow-hidden.border-b.border-gray-200(class="sm:rounded-lg")
        table.min-w-full.divide-y.divide-gray-200
          thead
            tr
              th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
                | Name
              th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
                | Email
              th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
                | Show
              th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
                | Edit
              / th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
              /   | Destroy
          tbody.bg-white.divide-y.divide-gray-200
            - @users.each do |user|
              tr
                td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
                  = user.name
                td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
                  = user.email
                td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
                  = link_to 'Show', user
                td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
                  = link_to 'Edit', edit_user_path(user), class: 'disabled'
                / td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
                /   = link_to 'Destroy', user, method: :delete, data: {confirm: 'Are you sure?'}
  .mx-8
    = link_to 'New User', new_user_path, class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled'

slim:app/views/users/show.html.slim


ruby:
  delete_modal_id = "id-#{SecureRandom.uuid}"
  delete_btn_id = "id-#{SecureRandom.uuid}"

p#notice
  = notice
.min-h-screen.bg-gray-100.m-10
  .bg-white.shadow.overflow-hidden.sm:rounded-lg
    .px-4.py-5.border-b.border-gray-200.sm:px-6
      h3.text-lg.leading-6.font-medium.text-gray-900
        |User details
      p.mt-1.max-w-2xl.text-sm.leading-5.text-gray-500
        |User details screen
      .px-4.py-5.sm:p-0
        dl
          .sm:grid.sm:grid-cols-3.sm:gap-4.sm:px-6.sm:py-5
            dt.text-sm.leading-5.font-medium.text-gray-500
              | Name:
            dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2
              = @user.name
              .mt-8.sm:mt-0.sm:grid.sm:grid-cols-3.sm:gap-4.sm:border-t.sm:border-gray-200.sm:px-6.sm:py-5
                dt.text-sm.leading-5.font-medium.text-gray-500
                  | Email:
                dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2
                  = @user.email
      .btn-wrapper
        button.bg-red-500.hover:bg-red-700.text-white.font-bold.py-2.px-4.m-3.rounded(
          type="button"
          data-modal-open="##{delete_modal_id}"
        )
          |Account deletion
    .pt-4
      = link_to 'Edit', edit_user_path(@user), class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled'
      |  |
      = link_to 'Back', users_path, class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'

.fixed.z-10.inset-0.overflow-y-auto.hidden(id="#{delete_modal_id}" data-modal)
  .flex.items-end.justify-center.min-h-screen.pt-4.px-4.pb-20.text-center.sm:block.sm:p-0
    .fixed.inset-0.transition-opacity
      .absolute.inset-0.bg-gray-700.opacity-75
    span.hidden.sm:inline-block.sm:align-middle.sm:h-screen
    .inline-block.align-bottom.bg-white.rounded-lg.px-4.pt-5.pb-4.text-left.overflow-hidden.shadow-xl.transform.transition-all.sm:my-8.sm:align-middle.sm:max-w-lg.sm:w-full.sm:p-6 aria-labelledby="modal-headline" aria-modal="true" role="dialog"
      .sm:flex.sm:items-start
        .mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-red-100.sm:mx-0.sm:h-10.sm:w-10
          svg.h-6.w-6.text-red-600 fill="none" stroke="currentColor" viewbox=("0 0 24 24")
            path d=("M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z") stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
        .mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
          h3#modal-headline.text-lg.leading-6.font-medium.text-gray-900
            |Account deletion
          .mt-2
            p.text-sm.leading-5.text-gray-500
              |To delete, "#{@user.name}Please press the account deletion button.
      .mt-5.mx-8
        input.shadow.appearance-none.border.rounded.w-full.py-2.px-3.text-gray-700.leading-tight.focus:outline-none.focus:shadow-outline(placeholder="Enter your user name" data-activation-target="##{delete_btn_id}" data-activation-value="#{@user.name}")
      .mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
        span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
          = link_to \
            @user,\
            method: :delete,\
            class: "disabled inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-red-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-red-500 focus:outline-none focus:border-red-700 focus:shadow-outline-red transition ease-in-out duration-150 sm:text-sm sm:leading-5",\
            tabindex: -1,\
            id: delete_btn_id do
            |Account deletion
        span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
          button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5(type="button" data-modal-close)
            |Cancel

Main subject

Modal partial implementation

app/javascript/src/js/modal.js


document.addEventListener('click', (e) => {
  const clicked = e.target.closest('[data-modal-open]')
  if (clicked) {
    const targetSelector = clicked.getAttribute('data-modal-open')
    const target = document.querySelector(targetSelector)
    if (target) {
      target.classList.remove('hidden')
    }
  }
})

document.addEventListener('click', (e) => {
  const clicked = e.target.closest('[data-modal-close]')
  if (clicked) {
    const targetSelector = clicked.getAttribute('data-modal-close')
    if (targetSelector) {
      const target = document.querySelector(targetSelector)
      if (target) {
        target.classList.add('hidden')
      }
    } else {
      const target = clicked.closest('[data-modal]')
      if (target) {
        target.classList.add('hidden')
      }
    }
  }
})

Enter your name or email address in the form to activate the delete button

app/javascript/src/scss/application.scss


@import "btn";

app/javascript/src/scss/_btn.scss


.disabled {
  @apply opacity-50 cursor-not-allowed pointer-events-none;
}

app/javascript/src/js/activation.js


document.addEventListener('input', (e) => {
  const input = e.target
  const targetSelector = input.dataset.activationTarget
  if (targetSelector) {
    const activationValue = input.dataset.activationValue
    const targets = document.querySelectorAll(targetSelector)
    if (input.value === activationValue) {
      targets.forEach((target) => {
        target.classList.remove('disabled')
        target.removeAttribute('tabindex')
      })
    } else {
      targets.forEach((target) => {
        target.classList.add('disabled')
        target.setAttribute('tabindex', -1)
      })
    }
  }
})

0cef6f0c3a5eb75bb9ab4a4a2c1cde5d.gif

Place the cursor on input-> click Tab-> click Enter I was able to delete the button even if it was disabled, so I avoided it with tabindex: -1.

Controller deletion process

          = link_to \
            @user,\
            method: :delete,\
            class: "disabled inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-red-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-red-500 focus:outline-none focus:border-red-700 focus:shadow-outline-red transition ease-in-out duration-150 sm:text-sm sm:leading-5",\
            tabindex: -1,\
            id: delete_btn_id do
            |Account deletion

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

Operation check

1ed7288e046e28d414c6c3fcbd312986.gif

Recommended Posts

Have them enter their name and email address before deleting the user account
Have the surname and given name entered separately and combine them before saving
Write ABNF in Java and pass the email address