diff --git a/app/javascript/components/user_mentor_memo.vue b/app/javascript/components/user_mentor_memo.vue
deleted file mode 100644
index 7c59c0d3f54..00000000000
--- a/app/javascript/components/user_mentor_memo.vue
+++ /dev/null
@@ -1,174 +0,0 @@
-
-section.a-card.is-memo.is-only-mentor
- header.card-header.is-sm(v-if='!editing && !productsMode')
- h2.card-header__title
- | メンター向けユーザーメモ
- hr.a-border-tint(v-if='!editing && !productsMode')
- .card-body(v-if='!editing')
- .card__description
- .a-long-text.is-md.a-placeholder(v-if='loading')
- p
- p
- p
- p
- p
- p
- .o-empty-message(v-else-if='memo.length === 0')
- .o-empty-message__icon
- i.fa-regular.fa-sad-tear
- .o-empty-message__text
- | ユーザーメモはまだありません。
- .a-long-text.is-md(v-else, v-html='markdownMemo')
- hr.a-border-tint(v-if='!editing')
- footer.card-footer(v-if='!editing')
- .card-main-actions
- .card-main-actions__items
- .card-main-actions__item
- button.card-footer-actions__action.a-button.is-sm.is-secondary.is-block(
- @click='editMemo')
- i.fa-solid.fa-pen
- | 編集
- .form-tabs.js-tabs(v-show='editing')
- .form-tabs__tab.js-tabs__tab(
- :class='{ "is-active": isActive("memo") }',
- @click='changeActiveTab("memo")')
- | メモ
- .form-tabs__tab.js-tabs__tab(
- :class='{ "is-active": isActive("preview") }',
- @click='changeActiveTab("preview")')
- | プレビュー
- .card-body(v-show='editing')
- .card__description
- .a-markdown-input.js-markdown-parent
- .a-markdown-input__inner.is-editor.js-tabs__content(
- :class='{ "is-active": isActive("memo") }')
- textarea.a-text-input.a-markdown-input__textarea(
- :id='`js-user-mentor-memo`',
- data-preview='#user-mentor-memo-preview',
- v-model='memo',
- name='user[memo]')
- .a-markdown-input__inner.is-preview.js-tabs__content(
- :class='{ "is-active": isActive("preview") }')
- .a-long-text.is-md.a-markdown-input__preview(v-html='markdownMemo')
- hr.a-border-tint(v-show='editing')
- .card-footer(v-show='editing')
- .card-main-actions
- .card-main-actions__items
- .card-main-actions__item
- button.a-button.is-sm.is-primary.is-block(@click='updateMemo')
- | 保存する
- .card-main-actions__item
- button.a-button.is-sm.is-secondary.is-block(@click='cancel')
- | キャンセル
-
-
-
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 0a07e55d1c1..9ee5b8eb3bf 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -55,6 +55,7 @@ import '../register-address.js'
import '../upload-image-to-article.js'
import '../header-dropdown.js'
import '../editor-selection-form.js'
+import '../user_mentor_memo.js'
import VueMounter from '../VueMounter.js'
import Announcements from '../components/announcements.vue'
@@ -66,7 +67,6 @@ import UsersAnswers from '../components/users-answers.vue'
import User from '../components/user.vue'
import Watches from '../components/watches.vue'
import WatchToggle from '../components/watch-toggle.vue'
-import UserMentorMemo from '../components/user_mentor_memo.vue'
import UserRecentReports from '../components/user-recent-reports.vue'
import Footprints from '../components/footprints.vue'
import QuestionAnswers from '../components/question-answers.vue'
@@ -87,7 +87,6 @@ mounter.addComponent(UsersAnswers)
mounter.addComponent(User)
mounter.addComponent(Watches)
mounter.addComponent(WatchToggle)
-mounter.addComponent(UserMentorMemo)
mounter.addComponent(UserRecentReports)
mounter.addComponent(Footprints)
mounter.addComponent(QuestionAnswers)
diff --git a/app/javascript/user_mentor_memo.js b/app/javascript/user_mentor_memo.js
new file mode 100644
index 00000000000..3aec4233a35
--- /dev/null
+++ b/app/javascript/user_mentor_memo.js
@@ -0,0 +1,144 @@
+import CSRF from 'csrf'
+import TextareaInitializer from 'textarea-initializer'
+import MarkdownInitializer from 'markdown-initializer'
+
+document.addEventListener('DOMContentLoaded', () => {
+ const mentorMemo = document.querySelector('.user-mentor-memo')
+ if (mentorMemo) {
+ TextareaInitializer.initialize('#js-user-mentor-memo')
+ const markdownInitializer = new MarkdownInitializer()
+ const userId = mentorMemo.dataset.user_id
+ let savedMemo = ''
+
+ const memoDisplay = mentorMemo.querySelector('.memo-display')
+ const memoEditor = mentorMemo.querySelector('.memo-editor')
+
+ const placeholder = memoDisplay.querySelector('.a-placeholder')
+ const emptyMessage = memoDisplay.querySelector('.o-empty-message')
+ const memoDisplayContent = memoDisplay.querySelector(
+ '.user-mentor-memo-content'
+ )
+ const memoEditorPreview = memoEditor.querySelector(
+ '.a-markdown-input__preview'
+ )
+ const editorTextarea = memoEditor.querySelector(
+ '.a-markdown-input__textarea'
+ )
+
+ fetch(`/api/users/${userId}.json`, {
+ method: 'GET',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest'
+ },
+ credentials: 'same-origin',
+ redirect: 'manual'
+ })
+ .then((response) => {
+ return response.json()
+ })
+ .then((json) => {
+ if (json.mentor_memo) {
+ savedMemo = json.mentor_memo
+ }
+ placeholder.classList.add('is-hidden')
+ if (savedMemo.length === 0) {
+ emptyMessage.classList.remove('is-hidden')
+ } else {
+ memoDisplayContent.classList.remove('is-hidden')
+ editorTextarea.value = savedMemo
+ switchMemoDisplay(memoDisplay, savedMemo)
+ memoDisplayContent.innerHTML = markdownInitializer.render(savedMemo)
+ memoEditorPreview.innerHTML = markdownInitializer.render(savedMemo)
+ }
+ })
+ .catch((error) => {
+ console.warn(error)
+ })
+
+ const editButton = memoDisplay.querySelector('.card-footer-actions__action')
+ const modalElements = [memoDisplay, memoEditor]
+ editButton.addEventListener('click', () =>
+ toggleClass(modalElements, 'is-hidden')
+ )
+
+ const saveButton = memoEditor.querySelector('.is-primary')
+ saveButton.addEventListener('click', () => {
+ toggleClass(modalElements, 'is-hidden')
+ savedMemo = editorTextarea.value
+ updateMemo(savedMemo, userId)
+ memoDisplayContent.innerHTML = markdownInitializer.render(savedMemo)
+ switchMemoDisplay(memoDisplay, savedMemo)
+ })
+
+ const cancelButton = memoEditor.querySelector('.is-secondary')
+ cancelButton.addEventListener('click', () => {
+ toggleClass(modalElements, 'is-hidden')
+ editorTextarea.value = savedMemo
+ memoEditorPreview.innerHTML = markdownInitializer.render(savedMemo)
+ })
+
+ editorTextarea.addEventListener('change', () => {
+ memoEditorPreview.innerHTML = markdownInitializer.render(
+ editorTextarea.value
+ )
+ })
+
+ const editorTab = memoEditor.querySelector('.editor-tab')
+ const editorTabContent = memoEditor.querySelector('.is-editor')
+ const previewTab = memoEditor.querySelector('.preview-tab')
+ const previewTabContent = memoEditor.querySelector('.is-preview')
+
+ const tabElements = [
+ editorTab,
+ editorTabContent,
+ previewTab,
+ previewTabContent
+ ]
+ editorTab.addEventListener('click', () =>
+ toggleClass(tabElements, 'is-active')
+ )
+ previewTab.addEventListener('click', () =>
+ toggleClass(tabElements, 'is-active')
+ )
+ }
+})
+
+function updateMemo(memo, userId) {
+ const params = {
+ user: {
+ mentor_memo: memo
+ }
+ }
+ fetch(`/api/mentor_memos/${userId}`, {
+ method: 'PUT',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'X-CSRF-Token': CSRF.getToken()
+ },
+ credentials: 'same-origin',
+ redirect: 'manual',
+ body: JSON.stringify(params)
+ })
+ .then((response) => {
+ return response
+ })
+ .catch((error) => {
+ console.warn(error)
+ })
+}
+
+function switchMemoDisplay(memoDisplay, memo) {
+ const memoDisplayContent = memoDisplay.querySelector(
+ '.user-mentor-memo-content'
+ )
+ const emptyMessage = memoDisplay.querySelector('.o-empty-message')
+ memoDisplayContent.classList.toggle('is-hidden', memo.length === 0)
+ emptyMessage.classList.toggle('is-hidden', memo.length !== 0)
+}
+
+function toggleClass(elements, className) {
+ elements.forEach((element) => {
+ element.classList.toggle(className)
+ })
+}
diff --git a/app/views/products/show.html.slim b/app/views/products/show.html.slim
index 5c749382290..d8de8816aa7 100644
--- a/app/views/products/show.html.slim
+++ b/app/views/products/show.html.slim
@@ -79,7 +79,7 @@ header.page-header
.user-info
= render 'users/user_secret_attributes', user: @product.user
= render 'users/metas', user: @product.user
- div(data-vue="UserMentorMemo" data-vue-user-id:number="#{@product.user.id}" data-vue-products-mode:boolean="#{true}")
+ = render 'users/user_mentor_memo', user_id: @product.user.id
.side-tabs-contents__item#side-tabs-content-4
div(data-vue="UserProducts" data-vue-user-id:number="#{@product.user.id}" data-vue-is-mentor:boolean="#{false}" data-vue-current-user-id:number="#{current_user.id}" data-vue-display-user-icon:boolean="false")
diff --git a/app/views/reports/show.html.slim b/app/views/reports/show.html.slim
index 3cc9691e9a0..eacbea64057 100644
--- a/app/views/reports/show.html.slim
+++ b/app/views/reports/show.html.slim
@@ -64,7 +64,7 @@
.user-info
= render 'users/user_secret_attributes', user: @report.user
= render 'users/metas', user: @report.user
- div(data-vue="UserMentorMemo" data-vue-user-id:number="#{@report.user_id}")
+ = render 'users/user_mentor_memo', user_id: @report.user_id
.side-tabs-contents__item#side-tabs-content-3.is-only-mentor
.card-list.a-card
- if @products.present?
diff --git a/app/views/talks/show.html.slim b/app/views/talks/show.html.slim
index 28c3d23f8c4..7a6cffb4c06 100644
--- a/app/views/talks/show.html.slim
+++ b/app/views/talks/show.html.slim
@@ -94,6 +94,6 @@
method: :patch,
data: { confirm: '本当によろしいですか?' },
class: 'a-button is-sm is-danger is-block'
- div(data-vue="UserMentorMemo" data-vue-user-id:number="#{@user.id}" data-vue-products-mode:boolean="#{true}")
+ = render 'users/user_mentor_memo', user_id: @user.id
.side-tabs-contents__item#side-tabs-content-2
= react_component('Reports', userId: @user.id, displayUserIcon: false, displayPagination: false)
diff --git a/app/views/users/_user_mentor_memo.html.slim b/app/views/users/_user_mentor_memo.html.slim
new file mode 100644
index 00000000000..688207c2600
--- /dev/null
+++ b/app/views/users/_user_mentor_memo.html.slim
@@ -0,0 +1,53 @@
+section.a-card.is-memo.is-only-mentor.user-mentor-memo data-user_id=user_id
+ .memo-display
+ .description
+ header.card-header.is-sm
+ h2.card-header__title
+ | メンター向けユーザーメモ
+ hr.a-border-tint
+ .card-body
+ .card__description
+ .a-long-text.is-md.a-placeholder
+ p
+ p
+ p
+ p
+ p
+ p
+ .o-empty-message.is-hidden
+ .o-empty-message__icon
+ i.fa-regular.fa-sad-tear
+ .o-empty-message__text
+ | ユーザーメモはまだありません。
+ .a-long-text.is-md.user-mentor-memo-content.is-hidden
+ hr.a-border-tint
+ footer.card-footer
+ .card-main-actions
+ .card-main-actions__items
+ .card-main-actions__item
+ button.card-footer-actions__action.a-button.is-sm.is-secondary.is-block
+ i.fa-solid.fa-pen
+ | 編集
+ .memo-editor.is-hidden
+ .form-tabs.js-tabs
+ .form-tabs__tab.js-tabs__tab.editor-tab.is-active
+ | メモ
+ .form-tabs__tab.js-tabs__tab.preview-tab
+ | プレビュー
+ .card-body
+ .card__description
+ .a-markdown-input.js-markdown-parent
+ .a-markdown-input__inner.is-editor.js-tabs__content.is-active
+ textarea.a-text-input.a-markdown-input__textarea id='js-user-mentor-memo' data-preview='#user-mentor-memo-preview' name='user[memo]'
+ .a-markdown-input__inner.is-preview.js-tabs__content
+ .a-long-text.is-md.a-markdown-input__preview
+ hr.a-border-tint
+ .card-footer
+ .card-main-actions
+ .card-main-actions__items
+ .card-main-actions__item
+ button.a-button.is-sm.is-primary.is-block
+ | 保存する
+ .card-main-actions__item
+ button.a-button.is-sm.is-secondary.is-block
+ | キャンセル
diff --git a/app/views/users/show.html.slim b/app/views/users/show.html.slim
index 096237e91a0..e5c4af25c86 100644
--- a/app/views/users/show.html.slim
+++ b/app/views/users/show.html.slim
@@ -68,7 +68,7 @@
.col-xs-12.col-lg-6.col-xxl-6
- if admin_or_mentor_login?
- div(data-vue="UserMentorMemo" data-vue-user-id:number="#{@user.id}")
+ = render 'users/user_mentor_memo', user_id: @user.id
- unless @user.total_learning_time.zero? || @user.mentor?
= react_component 'Grass', { userId: @user.id }, { class: 'a-card' }
- if @user.student_or_trainee?