diff --git a/app/javascript/components/Notification.jsx b/app/javascript/components/Notification.jsx
new file mode 100644
index 00000000000..dbe05dcb380
--- /dev/null
+++ b/app/javascript/components/Notification.jsx
@@ -0,0 +1,62 @@
+import React from 'react'
+import dayjs from 'dayjs'
+import ja from 'dayjs/locale/ja'
+dayjs.locale(ja)
+
+export default function Notification({ notification }) {
+ const createdAt = dayjs(notification.created_at).format(
+ 'YYYY年MM月DD日(ddd) HH:mm'
+ )
+ const roleClass = `is-${notification.sender.primary_role}`
+
+ return (
+
+
+
+
+
+
+
+
+
+ {notification.read === false && (
+
+ 未読
+
+ )}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/javascript/components/Notifications.jsx b/app/javascript/components/Notifications.jsx
new file mode 100644
index 00000000000..f725fca2c29
--- /dev/null
+++ b/app/javascript/components/Notifications.jsx
@@ -0,0 +1,113 @@
+import React from 'react'
+import Notification from './Notification'
+import LoadingListPlaceholder from './LoadingListPlaceholder'
+import Pagination from './Pagination'
+import UnconfirmedLink from './UnconfirmedLink'
+import useSWR from 'swr'
+import fetcher from '../fetcher'
+import usePage from './hooks/usePage'
+
+export default function Notifications({ isMentor }) {
+ const per = 20
+ const isUnreadPage = () => {
+ const params = new URLSearchParams(location.search)
+ return params.get('status') !== null && params.get('status') === 'unread'
+ }
+ const url = () => {
+ const params = new URLSearchParams(location.search)
+ return `/api/notifications.json?${params}`
+ }
+
+ const { data, error } = useSWR(url, fetcher)
+
+ const { page, setPage } = usePage()
+
+ if (error) {
+ console.warn(error)
+ return failed to load
+ }
+
+ if (!data) {
+ return (
+
+
+
+ )
+ } else if (data.notifications.length === 0) {
+ return (
+
+
+
+
+
+ {isUnreadPage ? '未読の通知はありません' : '通知はありません'}
+
+
+ )
+ } else {
+ const params = new URLSearchParams(location.search)
+ return (
+ <>
+
+
+ {data.total_pages > 1 && (
+
+ )}
+
+ {data.notifications.map((notification) => {
+ return (
+
+ )
+ })}
+
+ {isMentor && isUnreadPage() && (
+
+ )}
+ {data.total_pages > 1 && (
+
+ )}
+
+ >
+ )
+ }
+}
diff --git a/app/javascript/notification.vue b/app/javascript/notification.vue
deleted file mode 100644
index 0bbf9352f5d..00000000000
--- a/app/javascript/notification.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-
-.card-list-item(:class='notification.read ? "is-read" : "is-unread"')
- .card-list-item__inner
- .card-list-item__user
- img.card-list-item__user-icon.a-user-icon(
- :title='notification.sender.icon_title',
- :src='notification.sender.avatar_url',
- :class='[roleClass]')
- .card-list-item__rows
- .card-list-item__row
- .card-list-item-title
- .card-list-item-title__start
- .a-list-item-badge.is-unread(v-if='notification.read === false')
- span
- | 未読
- h2.card-list-item-title__title(itemprop='name')
- a.card-list-item-title__link.a-text-link.js-unconfirmed-link(
- :href='notification.path',
- itemprop='url')
- span.card-list-item-title__link-label
- | {{ notification.message }}
- .card-list-item__row
- .card-list-item-meta
- .card-list-item-meta__items
- .card-list-item-meta__item
- time.a-meta(:datetime='notification.created_at')
- | {{ formattedCreatedAtInJapanese }}
-
-
diff --git a/app/javascript/notifications.js b/app/javascript/notifications.js
deleted file mode 100644
index cc93b84557b..00000000000
--- a/app/javascript/notifications.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import Vue from 'vue'
-import Notifications from 'notifications.vue'
-
-document.addEventListener('DOMContentLoaded', () => {
- const selector = '#js-notifications'
- const notifications = document.querySelector(selector)
- if (notifications) {
- const isMentor = notifications.getAttribute('data-is-mentor')
- const target = notifications.getAttribute('data-target')
- new Vue({
- render: (h) =>
- h(Notifications, {
- props: {
- // 文字列を真偽値に変換して渡す
- isMentor: isMentor === 'true',
- target: target
- }
- })
- }).$mount(selector)
- }
-})
diff --git a/app/javascript/notifications.vue b/app/javascript/notifications.vue
deleted file mode 100644
index e712784b5eb..00000000000
--- a/app/javascript/notifications.vue
+++ /dev/null
@@ -1,130 +0,0 @@
-
-#notifications.page-content.loaing(v-if='!loaded')
- loadingListPlaceholder
-.o-empty-message(v-else-if='notifications.length === 0')
- .o-empty-message__icon
- i.fa-regular.fa-smile
- p.o-empty-message__text(v-if='isUnreadPage')
- | 未読の通知はありません
- p.o-empty-message__text(v-else)
- | 通知はありません
-#notifications.page-content.loaded(v-else)
- nav.pagination(v-if='totalPages > 1')
- pager(v-bind='pagerProps')
- .card-list.a-card
- notification(
- v-for='notification in notifications',
- :key='notification.id',
- :notification='notification')
- unconfirmed-links-open-button(
- v-if='isMentor && isUnreadPage',
- label='未読の通知を一括で開く')
- nav.pagination(v-if='totalPages > 1')
- pager(v-bind='pagerProps')
-
-
-
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index eeecca08434..666dfe648a5 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -29,7 +29,6 @@ import '../github_grass'
import '../following.js'
import '../hide-user.js'
import '../categories-practice.js'
-import '../notifications.js'
import '../products.js'
import '../courses-categories.js'
import '../courses-practices.js'
diff --git a/app/views/notifications/index.html.slim b/app/views/notifications/index.html.slim
index a8590bb6a9b..738ce06b75d 100644
--- a/app/views/notifications/index.html.slim
+++ b/app/views/notifications/index.html.slim
@@ -42,11 +42,4 @@ main.page-main
hr.a-border
.page-body
.container.is-md
- nav.pill-nav
- .container
- ul.pill-nav__items
- li.pill-nav__item
- = link_to '未読', notifications_path(status: 'unread', target: @target), class: "pill-nav__item-link #{params[:status] == 'unread' ? 'is-active' : ''}"
- li.pill-nav__item
- = link_to '全て', notifications_path(target: @target), class: "pill-nav__item-link #{params[:status] == 'unread' ? '' : 'is-active'}"
- #js-notifications(data-is-mentor="#{mentor_login?}" data-target="#{@target}")
+ = react_component('Notifications', isMentor: mentor_login?)
diff --git a/test/system/notifications_test.rb b/test/system/notifications_test.rb
index a0e72257ccb..461961ea163 100644
--- a/test/system/notifications_test.rb
+++ b/test/system/notifications_test.rb
@@ -88,7 +88,7 @@ class NotificationsTest < ApplicationSystemTestCase
login_user 'mentormentaro', 'testtest'
visit '/notifications'
within first('nav.pagination') do
- find('a', text: '2').click
+ find('button', text: '2').click
end
# 2ページ目に1番古い通知が表示されることを確認
assert_text '1番古い通知'
@@ -123,7 +123,7 @@ class NotificationsTest < ApplicationSystemTestCase
login_user 'mentormentaro', 'testtest'
visit '/notifications?status=unread'
within first('nav.pagination') do
- find('a', text: '2').click
+ find('button', text: '2').click
end
assert_text '1番古い通知'
assert_no_text '1番新しい通知'
@@ -156,7 +156,7 @@ class NotificationsTest < ApplicationSystemTestCase
login_user 'mentormentaro', 'testtest'
visit '/notifications?status=unread&target=mention'
within first('nav.pagination') do
- find('a', text: '2').click
+ find('button', text: '2').click
end
assert_text '1番古い通知'
assert_no_text '1番新しい通知'
@@ -218,8 +218,9 @@ class NotificationsTest < ApplicationSystemTestCase
login_user 'mentormentaro', 'testtest'
visit '/notifications?page=2'
within first('nav.pagination') do
- find('a', text: '1').click
+ find('button', text: '1').click
end
+ assert_text '1番新しい通知'
page.go_back
assert_text '1番古い通知'
assert_no_text '1番新しい通知'