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

ユーザー個別ページ > 回答一覧をVue.js化した #4319

Merged
merged 25 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ae4888c
一旦コミット(表示されてない)
eatplaynap Feb 28, 2022
1dc77d8
動作するようにjbuilderを書き換え
eatplaynap Mar 1, 2022
60fbf42
ページャー追加
eatplaynap Mar 17, 2022
a1e8d81
ページのタイトル変更
eatplaynap Mar 17, 2022
dda28e9
必要のないpropsを削除
eatplaynap Mar 18, 2022
85b3010
テスト追加
eatplaynap Mar 18, 2022
e6fbb3d
prettier通過
eatplaynap Mar 18, 2022
4823061
落ちていたテストの修正
eatplaynap Mar 18, 2022
5a2e4c0
正しいクラス名が表示されるよう変更
eatplaynap Mar 21, 2022
af72fef
質問の更新日時が表示されるよう変更
eatplaynap Mar 21, 2022
8418a12
default値を空文字からnullに変更
eatplaynap Mar 21, 2022
c3a1721
lint通過
eatplaynap Mar 21, 2022
7613bd5
間違えて修正してしまったファイルを戻す
eatplaynap Mar 21, 2022
2479ec3
名前をリンクにした
eatplaynap Mar 22, 2022
89f0610
Vue化でいらなくなったファイル・コードの削除
eatplaynap Mar 22, 2022
630896d
回答一覧からユーザー個別ページへのリンクのテスト追加
eatplaynap Mar 22, 2022
e18be5d
lint通過
eatplaynap Mar 22, 2022
7b11fbf
回答の更新日時が一覧画面で表示されるよう修正
eatplaynap Mar 23, 2022
6be0e67
ページャー確認用デモ回答を追加
eatplaynap Mar 23, 2022
007e1f8
ymlファイルのインデント修正
eatplaynap Mar 24, 2022
539f2e9
AnswerのAPIにユーザーIDでの絞り込み機能を追加
eatplaynap Mar 30, 2022
633942a
ユーザー一覧からのアクセスOK(回答個別ページからのanswersへのアクセスができなくなった)
eatplaynap Apr 1, 2022
bd7602c
誤って消していたusersのルーティングを復活
eatplaynap Apr 1, 2022
7ab5deb
不要なコントローラーを削除
eatplaynap Apr 1, 2022
dc3ed76
テスト修正
eatplaynap Apr 1, 2022
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
17 changes: 16 additions & 1 deletion app/controllers/api/answers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,22 @@ class API::AnswersController < API::BaseController
before_action :set_available_emojis, only: %i[index create]

def index
@answers = question.answers.order(created_at: :asc)
if params[:question_id].present?
@answers = question.answers.order(created_at: :asc)
else
user = User.find(params[:user_id])
@answers = user.answers.where(user_id: params[:user_id]).includes(
{
question: [
:correct_answer,
{ user: [:company, { avatar_attachment: :blob }] },
:practice,
:tag_taggings,
:tags
]
}
).order(created_at: :desc).page(params[:page])
end
end

def create
Expand Down
11 changes: 0 additions & 11 deletions app/controllers/users/answers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,5 @@ class Users::AnswersController < ApplicationController

def index
@user = User.find(params[:user_id])
@answers = @user.answers.includes(
{
question: [
:correct_answer,
{ user: [:company, { avatar_attachment: :blob }] },
:practice,
:tag_taggings,
:tags
]
}
).order(created_at: :desc)
end
end
2 changes: 1 addition & 1 deletion app/javascript/answers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export default {
return response.json()
})
.then((json) => {
json.forEach((c) => {
json.answers.forEach((c) => {
this.answers.push(c)
})

Expand Down
1 change: 1 addition & 0 deletions app/javascript/packs/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ import '../learning-completion-message.js'
import '../practices-pages.js'
import '../users-questions.js'
import '../company-select.js'
import '../users-answers.js'
67 changes: 67 additions & 0 deletions app/javascript/users-answer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template lang="pug">
.thread-list-item(:class='answerClass')
.thread-list-item__inner
.thread-list-item__user
a.a-user-name(:href='answer.question.user.url')
img.thread-list-item__user-icon.a-user-icon(
:title='answer.question.user.icon_title',
:alt='answer.question.user.icon_title',
:src='answer.question.user.avatar_url',
:class='[roleClass, daimyoClass]'
)
.thread-list-item__rows
.thread-list-item__row
.thread-list-item-title
h1.thread-list-item-title__title(itemprop='name')
a.thread-list-item-title__link(
:href='answer.question.url',
itemprop='url'
) {{ answer.question.title }}
.thread-list-item__row(v-if='answer.question.practice')
.thread-list-item-meta
.thread-list-item-meta__items
.thread-list-item-meta__item
.thread-list-item-sub-title {{ answer.question.practice.title }}
.thread-list-item__row
.thread-list-item__summary
p {{ answer.description }}
.thread-list-item__row
.thread-list-item-meta
.thread-list-item-meta__items
.thread-list-item-meta__item
a.a-user-name(:href='`/users/${answer.question.user.id}`')
| {{ answer.question.user.long_name }}
.thread-list-item-meta__item
time.a-meta(:datetime='answer.updated_at', pubdate='pubdate') {{ updatedAt }}
.answer-badge(v-if='answer.type == "CorrectAnswer"')
.answer-badge__icon
i.fas.fa-star
.answer-badge__label ベストアンサー
</template>
<script>
import dayjs from 'dayjs'

export default {
props: {
answer: { type: Object, required: true }
},
computed: {
updatedAt() {
return dayjs(this.answer.updated_at).format('YYYY年MM月DD日(dd) HH:mm')
},
roleClass() {
return `is-${this.answer.question.user.primary_role}`
},
daimyoClass() {
return { 'is-daimyo': this.answer.question.user.daimyo }
},
answerClass() {
if (this.answer.has_correct_answer) {
return 'is-solved'
} else {
return ''
}
}
}
}
</script>
18 changes: 18 additions & 0 deletions app/javascript/users-answers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Vue from 'vue'
import UsersAnswers from './users-answers.vue'

document.addEventListener('DOMContentLoaded', () => {
const selector = '#js-users-answers'
const usersAnswers = document.querySelector(selector)
if (usersAnswers) {
const userId = usersAnswers.getAttribute('data-user-id')
new Vue({
render: (h) =>
h(UsersAnswers, {
props: {
userId
}
})
}).$mount(selector)
}
})
106 changes: 106 additions & 0 deletions app/javascript/users-answers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<template lang="pug">
div
nav.pagination(v-if='totalPages > 1')
pager(v-bind='pagerProps')
div(v-if='answers === null')
loadingListPlaceholder
.thread-list.a-card
.o-empty-message(v-else-if='answers.length === 0')
.o-empty-message__icon
i.far.fa-sad-tear
p.o-empty-message__text
| 回答はまだありません。
.thread-list.a-card(v-else)
usersAnswer(v-for='answer in answers', :key='answer.id', :answer='answer')
nav.pagination(v-if='totalPages > 1')
pager(v-bind='pagerProps')
</template>

<script>
import LoadingListPlaceholder from './loading-list-placeholder.vue'
import Pager from './pager.vue'
import UsersAnswer from './users-answer.vue'

export default {
components: {
loadingListPlaceholder: LoadingListPlaceholder,
pager: Pager,
usersAnswer: UsersAnswer
},
props: {
userId: { type: String, required: true }
},
data() {
return {
answers: null,
currentPage: this.pageParam(),
totalPages: null
}
},
computed: {
newParams() {
const params = new URL(location.href).searchParams
params.set('page', this.currentPage)
params.set('user_id', this.userId)
return params
},
newURL() {
return `${location.pathname}?${this.newParams}`
},
usersAnswersAPI() {
const params = this.newParams
return `/api/answers.json?${params}`
},
pagerProps() {
return {
initialPageNumber: this.currentPage,
pageCount: this.totalPages,
pageRange: 9,
clickHandle: this.clickCallback
}
}
},
created() {
window.onpopstate = () => {
this.currentPage = this.pageParam()
this.getUsersAnswers()
}
this.getUsersAnswers()
},
methods: {
pageParam() {
const url = new URL(location.href)
const page = url.searchParams.get('page')
return parseInt(page || 1)
},
clickCallback(pageNum) {
this.currentPage = pageNum
history.pushState(null, null, this.newURL)
this.usersAnswers = null
this.getUsersAnswers()
},
getUsersAnswers() {
fetch(this.usersAnswersAPI, {
method: 'GET',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
credentials: 'same-origin',
redirect: 'manual'
})
.then((response) => {
return response.json()
})
.then((json) => {
this.answers = []
json.answers.forEach((r) => {
this.answers.push(r)
console.log(this.answers)
})
this.totalPages = parseInt(json.totalPages)
})
.catch((error) => {
console.warn(error)
})
}
}
}
</script>
3 changes: 2 additions & 1 deletion app/views/api/answers/_answer.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
json.(answer, :id, :description, :created_at, :updated_at, :type, :question_id)
json.has_correct_answer answer.question.correct_answer.present?
json.question do
json.created_at answer.question.created_at
json.partial! 'api/questions/question', question: answer.question
end
json.user do
json.partial! "api/users/user", user: answer.user
Expand Down
6 changes: 5 additions & 1 deletion app/views/api/answers/index.json.jbuilder
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
json.array! @answers do |answer|
json.answers @answers do |answer|
json.partial! "api/answers/answer", answer: answer, available_emojis: @available_emojis
end

if @answers.respond_to?(:total_pages)
json.totalPages @answers.total_pages
end
36 changes: 0 additions & 36 deletions app/views/users/answers/_answer.html.slim

This file was deleted.

12 changes: 2 additions & 10 deletions app/views/users/answers/index.html.slim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- title "#{@user.login_name}の回答"
- title @user.login_name
header.page-header
.container
.page-header__inner
Expand All @@ -14,12 +14,4 @@ header.page-header

.page-body
.container.is-md
- if @answers.present?
.thread-list.a-card
= render partial: 'users/answers/answer', collection: @answers, as: :answer
- else
.o-empty-message
.o-empty-message__icon
i.far.fa-sad-tear
p.o-empty-message__text
| 回答はまだありません。
#js-users-answers data-user-id="#{params[:user_id]}"
7 changes: 7 additions & 0 deletions db/fixtures/answers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,10 @@ answer6:
description: Q&Aのコメントです。(ベストアンサーではない)
user: machida
question: question6

<% 7.upto(50) do |i| %>
answer<%= i %>:
description: ページャー確認用のデモ回答です。
user: komagata
question: question1
<% end %>
16 changes: 16 additions & 0 deletions test/integration/api/answers_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require 'test_helper'

class API::AnswersTest < ActionDispatch::IntegrationTest
test 'GET /api/answers.json?user_id=253826460' do
user = users(:hajime)
get api_answers_path(user_id: user.id, format: :json)
assert_response :unauthorized

token = create_token('hajime', 'testtest')
get api_answers_path(user_id: user.id, format: :json),
headers: { 'Authorization' => "Bearer #{token}" }
assert_response :ok
end
end
10 changes: 9 additions & 1 deletion test/system/user/answers_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
class User::AnswersTest < ApplicationSystemTestCase
test 'show listing answers' do
visit_with_auth "/users/#{users(:komagata).id}/answers", 'komagata'
assert_equal 'komagataの回答 | FJORD BOOT CAMP(フィヨルドブートキャンプ)', title
assert_equal 'komagata | FJORD BOOT CAMP(フィヨルドブートキャンプ)', title
end

test "visit user profile page when clicked on user's name on answer" do
visit_with_auth "/users/#{users(:komagata).id}/answers", 'komagata'
assert_text 'どのエディターを使うのが良いでしょうか'
click_link 'machida (Machida Teppei)', match: :first
assert_text 'プロフィール'
assert_text 'Machida Teppei(マチダ テッペイ)'
end
end