Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

卒業証書のPDFファイルをアップロードできる機能を追加 #8190

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/controllers/admin/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def show
def edit; end

def update
@user.diploma_file = nil if params[:user][:remove_diploma] == '1'
if @user.update(user_params)
destroy_subscription(@user)
Newspaper.publish(:retirement_create, { user: @user }) if @user.saved_change_to_retired_on?
Expand Down Expand Up @@ -67,7 +68,7 @@ def user_params
:job_seeker, :github_collaborator,
:officekey_permission, :tag_list, :training_ends_on,
:auto_retire, :invoice_payment, :hide_mentor_profile,
:profile_image, :profile_name, :profile_job, :mentor,
:profile_image, :profile_name, :profile_job, :mentor, :diploma_file,
:profile_text, { authored_books_attributes: %i[id title url cover _destroy] },
:country_code, :subdivision_code, discord_profile_attributes: %i[account_name times_url times_id]
)
Expand Down
40 changes: 40 additions & 0 deletions app/javascript/fileinput.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,45 @@ function extractField(elements) {
}
}

function initializePdfUploadField() {
Copy link
Contributor

@mousu-a mousu-a Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

提案です!
initializeDiplomaUploadFieldの方がいいかも?

const uploadField = document.getElementById('js-pdf-upload-field')
if (!uploadField) return

const removeButton = document.getElementById('js-remove-pdf-button')
const fileLink = document.getElementById('js-pdf-file-link')
const removeFlag = document.getElementById('js-remove-pdf-flag')
const fileNameDisplay = document.getElementById('js-pdf-name')
Copy link
Contributor

@mousu-a mousu-a Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

提案です!
fileNameDisplay → fileName でも良さそうです🙆

const fileInput = uploadField.querySelector('input[type="file"]')

const updateFileNameDisplay = (name = '') => {
Copy link
Contributor

@mousu-a mousu-a Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

提案です!
updateDisplayedFileName とかではどうでしょうか?

if (name) {
Copy link
Contributor

@mousu-a mousu-a Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こんな感じで書けそうです🙆

fileNameDisplay.textContent = name
const displayedStatus = name ? 'block' : 'none'
fileNameDisplay.style.display = displayedStatus

Copy link
Contributor Author

@hagiya0121 hagiya0121 Dec 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

指摘された点とfileInputのイベントリスナーのロジックもリファクタリングしました。
97b19ed

fileNameDisplay.textContent = name
fileNameDisplay.style.display = 'block'
} else {
fileNameDisplay.textContent = ''
fileNameDisplay.style.display = 'none'
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

今回実装する部分を関数にして↑の DOMContentLoadedに入れてはどうでしょうか?

あと画面の読み込み時に実行していますが、該当する画面でない場合のreturnがなさそうです。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変更しました
6b5d653

removeButton.addEventListener('click', () => {
if (fileLink) fileLink.style.display = 'none'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ファイルがアップロードされている時のみ削除ボタンが表示されていれば、if文は不要になるかもです

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fileLinkはHTMLが生成されない場合があるのでエラーになるようです。

uploadField.style.display = 'flex'
fileInput.value = ''
removeFlag.value = '1'
updateFileNameDisplay()
})

fileInput.addEventListener('change', () => {
if (fileInput.files && fileInput.files[0]) {
const fileName = fileInput.files[0].name
updateFileNameDisplay(fileName)
removeFlag.value = '0'
} else {
updateFileNameDisplay()
}
})
}

document.addEventListener('DOMContentLoaded', () => {
const ref = document.querySelector('#reference_books')
if (ref) {
Expand All @@ -84,4 +123,5 @@ document.addEventListener('DOMContentLoaded', () => {
})
}
initializeFileInput(document)
initializePdfUploadField()
})
1 change: 1 addition & 0 deletions app/javascript/stylesheets/_common-imports.sass
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
@import "atoms/a-elapsed-days"
@import "atoms/a-empty-message"
@import "atoms/a-file-input"
@import "atoms/a-pdf-input"
@import "atoms/a-form-frame"
@import "atoms/a-form-help"
@import "atoms/a-form-label"
Expand Down
85 changes: 43 additions & 42 deletions app/javascript/stylesheets/atoms/_a-file-input.sass
Original file line number Diff line number Diff line change
@@ -1,47 +1,48 @@
.a-file-input
label
height: 10rem
max-width: 100%
margin-inline: auto
border: solid 1px var(--input-border)
background-color: var(--base)
display: flex
align-items: center
justify-content: center
+position(relative)
overflow: hidden
cursor: pointer
border-radius: .25rem
padding: .5rem .5rem 2.75rem .5rem
&.is-thumbnail
label
height: 15rem
img
max-height: 100%
transition: all .2s ease-out
&:hover
opacity: .6
&.is-square img
+size(7rem)
object-fit: cover
&.is-book img
max-height: 100%
max-width: 100%
object-fit: contain
border: solid 1px var(--border-tint)
.a-pdf-input
display: flex
gap: .75rem
+media-breakpoint-down(sm)
flex-direction: column

.a-pdf-input__inner
flex: 1
background-color: var(--base)
display: flex
align-items: center
justify-content: center
+position(relative)
overflow: hidden
cursor: pointer
border-radius: .25rem
height: 2.25rem
input
overflow: hidden
+size(0)
+position(absolute, left 0, top 0)
opacity: 0
p
+size(100% 2.25rem)
background-color: var(--base)
+position(absolute, left 0, bottom 0, right 0)
+text-block(.8125rem 1, flex 600)
justify-content: center
align-items: center
border-top: solid 1px var(--input-border)
transition: all .2s ease-out
&:hover
background-color: #e8e8e8

a.a-pdf-input__inner
text-decoration: none
color: var(--default-text)

.a-pdf-input__file
padding: .5rem
flex: 1
border-radius: .25rem 0 0 .25rem
border: solid 1px var(--input-border)
border-right: none
height: 2.25rem

.a-pdf-input__file-name
font-size: .875rem
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
display: block
+media-breakpoint-up(md)
max-width: 18rem
+media-breakpoint-down(sm)
max-width: 8rem

.a-pdf-input__upload.a-button
border-radius: 0 .25rem .25rem 0
48 changes: 48 additions & 0 deletions app/javascript/stylesheets/atoms/_a-pdf-input.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.a-pdf-input
display: flex
gap: .75rem
+media-breakpoint-down(sm)
flex-direction: column

.a-pdf-input__inner
flex: 1
background-color: var(--base)
display: flex
align-items: center
justify-content: center
+position(relative)
overflow: hidden
cursor: pointer
border-radius: .25rem
height: 2.25rem
input
overflow: hidden
+size(0)
+position(absolute, left 0, top 0)
opacity: 0

a.a-pdf-input__inner
text-decoration: none
color: var(--default-text)

.a-pdf-input__file
padding: .5rem
flex: 1
border-radius: .25rem 0 0 .25rem
border: solid 1px var(--input-border)
border-right: none
height: 2.25rem

.a-pdf-input__file-name
font-size: .875rem
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
display: block
+media-breakpoint-up(md)
max-width: 18rem
+media-breakpoint-down(sm)
max-width: 8rem

.a-pdf-input__upload.a-button
border-radius: 0 .25rem .25rem 0
2 changes: 2 additions & 0 deletions app/javascript/stylesheets/config/mixins/_button.sass
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
background-color: $color
color: $text-color
border-color: $border-color
&.is-secondary
border-color: var(--input-border)
&:focus,
&:active
box-shadow: 0 0 0 .1875rem rgba($color, .25)
Expand Down
3 changes: 3 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ class User < ApplicationRecord

has_one_attached :avatar
has_one_attached :profile_image
has_one_attached :diploma_file

after_create UserCallbacks.new

Expand Down Expand Up @@ -207,6 +208,8 @@ class User < ApplicationRecord
message: 'はPNG, JPG, GIF, HEIC, HEIF形式にしてください'
}

validates :diploma_file, content_type: { in: ['application/pdf'], message: 'はPDF形式にしてください' }

validates :country_code, inclusion: { in: ISO3166::Country.codes }, allow_blank: true

validates :subdivision_code, inclusion: { in: ->(user) { user.subdivision_codes } }, allow_blank: true, if: -> { country_code.present? }
Expand Down
20 changes: 20 additions & 0 deletions app/views/users/_form.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@
.form-item-block__item
= render 'users/form/graduate', f: f

.form-item
= f.hidden_field :remove_diploma, value: '0', id: 'js-remove-pdf-flag'
= f.label :diploma_file, class: 'a-form-label'
.a-pdf-input
- if @user.diploma_file.attached?
= link_to url_for(@user.diploma_file), class: 'a-pdf-input__inner', id: 'js-pdf-file-link', target: '_blank', rel: 'noopener' do
.a-pdf-input__file
span.a-pdf-input__file-name
= @user.diploma_file.filename
.a-pdf-input__upload.a-button.is-md.is-secondary
| PDFを確認
- display_style = @user.diploma_file.attached? ? 'display: none;' : 'display: flex;'
label.a-pdf-input__inner#js-pdf-upload-field(style="#{display_style}")
= f.file_field :diploma_file
.a-pdf-input__file
span.a-pdf-input__file-name#js-pdf-name
.a-pdf-input__upload.a-button.is-md.is-secondary
| PDFを選択
= button_tag '削除', type: 'button', class: 'a-button is-md is-secondary', id: 'js-remove-pdf-button'

Copy link
Contributor

@mousu-a mousu-a Dec 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

button_tagが使えそうです!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変更しました
3aeb25a

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ファイルがアップロードされていない状態で削除ボタンがあるのはおかしい気がします🤔

.form-item-block
.form-item-block__inner
header.form-item-block__header
Expand Down
1 change: 1 addition & 0 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ ja:
other_editor: その他のエディタ
hide_mentor_profile: プロフィール非公開
invoice_payment: 「請求書払い」
diploma_file: 卒業証書(PDF)
discord_profile:
account_name: Discord アカウント
times_url: 分報URL
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/files/users/diplomas/diploma.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>卒業証書</title>
</head>
<body>
<h1>卒業証書</h1>
</body>
</html>
Binary file added test/fixtures/files/users/diplomas/diploma.pdf
Binary file not shown.
35 changes: 35 additions & 0 deletions test/system/admin/users_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -352,4 +352,39 @@ class Admin::UsersTest < ApplicationSystemTestCase
visit_with_auth "/admin/users/#{user.id}/edit", 'komagata'
assert has_checked_field?('user_hide_mentor_profile', visible: false)
end

test 'shows diploma link on edit page after upload' do
user = users(:kimura)
visit_with_auth edit_admin_user_path(user), 'komagata'
attach_file 'user[diploma_file]', file_fixture('users/diplomas/diploma.pdf'), visible: false
click_on '更新する'
assert_text 'ユーザー情報を更新しました'
assert user.reload.diploma_file.attached?
visit edit_admin_user_path(user)
assert_link 'diploma.pdf'
end

test 'admin can delete diploma file' do
user = users(:kimura)
visit_with_auth edit_admin_user_path(user), 'komagata'
attach_file 'user[diploma_file]', file_fixture('users/diplomas/diploma.pdf'), visible: false
click_button '更新する'
assert_text 'ユーザー情報を更新しました'
assert user.reload.diploma_file.attached?
visit edit_admin_user_path(user)
click_on '削除'
click_on '更新する'
assert_text 'ユーザー情報を更新しました'
assert_not user.reload.diploma_file.attached?
visit edit_admin_user_path(user)
assert_no_link 'diploma.pdf'
end

test 'rejects diploma upload if not PDF format' do
user = users(:kimura)
visit_with_auth edit_admin_user_path(user), 'komagata'
attach_file 'user[diploma_file]', file_fixture('users/diplomas/diploma.html'), visible: false
click_on '更新する'
assert_text '卒業証書(PDF)はPDF形式にしてください'
end
end