Skip to content

Commit

Permalink
Merge pull request #7170 from fjordllc/chore/convert-generation-users…
Browse files Browse the repository at this point in the history
…-vue-component-to-react

generation-users.vueと依存するVueファイルをReactコンポーネントに書き換え
  • Loading branch information
komagata authored Apr 15, 2024
2 parents d361c24 + bf53c76 commit e6f3de4
Show file tree
Hide file tree
Showing 16 changed files with 664 additions and 166 deletions.
157 changes: 157 additions & 0 deletions app/javascript/components/Following.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React, { useState, useRef } from 'react'
import CSRF from '../csrf.js'

export default function Following({ isFollowing, userId, isWatching }) {
const [following, setFollowing] = useState(isFollowing)
const [watching, setWatching] = useState(isWatching)
const followingDetailsRef = useRef(null)

const followOrChangeFollow = (watch) => {
const params = { id: userId, watch: watch }
const method = following ? 'PATCH' : 'POST'
const url = following ? `/api/followings/${userId}` : `/api/followings`
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': CSRF.getToken()
},
credentials: 'same-origin',
redirect: 'manual',
body: JSON.stringify(params)
})
.then((response) => {
if (response.ok) {
setFollowing(true)
setWatching(watch)
} else {
alert('フォロー処理に失敗しました')
}
})
.catch((error) => {
console.warn(error)
})
.finally(() => {
followingDetailsRef.current.open = false
})
}

const unfollow = () => {
const params = { id: userId }
fetch(`/api/followings/${userId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json; charset=utf-8',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': CSRF.getToken()
},
credentials: 'same-origin',
redirect: 'manual',
body: JSON.stringify(params)
})
.then((response) => {
if (response.ok) {
setFollowing(false)
setWatching(false)
} else {
alert('フォロー処理に失敗しました')
}
})
.catch((error) => {
console.warn(error)
})
.finally(() => {
followingDetailsRef.current.open = false
})
}

return (
<details ref={followingDetailsRef} className="following">
<summary className="following__summary">
{following && watching ? (
<span className="a-button is-warning is-sm is-block">
<i className="fa-solid fa-check"></i>
<span>コメントあり</span>
</span>
) : following && !watching ? (
<span className="a-button is-warning is-sm is-block">
<i className="fa-solid fa-check"></i>
<span>コメントなし</span>
</span>
) : (
<span className="a-button is-secondary is-sm is-block">
フォローする
</span>
)}
</summary>
<div className="following__dropdown a-dropdown">
<ul className="a-dropdown__items">
<li className="following__dropdown-item a-dropdown__item">
{following && watching ? (
<button className="following-option a-dropdown__item-inner is-active">
<div className="following-option__inner">
<div className="following-option__label">コメントあり</div>
<div className="following-option__desciption">
フォローしたユーザーの日報を自動でWatch状態にします。日報投稿時の通知と日報にコメントが来た際に通知を受け取ります。
</div>
</div>
</button>
) : (
<button
className="following-option a-dropdown__item-inner"
onClick={() => followOrChangeFollow(true)}>
<div className="following-option__inner">
<div className="following-option__label">コメントあり</div>
<div className="following-option__desciption">
フォローしたユーザーの日報を自動でWatch状態にします。日報投稿時の通知と日報にコメントが来た際に通知を受け取ります。
</div>
</div>
</button>
)}
</li>
<li className="following__dropdown-item a-dropdown__item">
{following && !watching ? (
<button className="following-option a-dropdown__item-inner is-active">
<div className="following-option__inner">
<div className="following-option__label">コメントなし</div>
<div className="following-option__desciption">
フォローしたユーザーの日報はWatch状態にしません。日報投稿時の通知だけ通知を受けとります。
</div>
</div>
</button>
) : (
<button
className="following-option a-dropdown__item-inner"
onClick={() => followOrChangeFollow(false)}>
<div className="following-option__inner">
<div className="following-option__label">コメントなし</div>
<div className="following-option__desciption">
フォローしたユーザーの日報はWatch状態にしません。日報投稿時の通知だけ通知を受けとります。
</div>
</div>
</button>
)}
</li>
<li className="following__dropdown-item a-dropdown__item">
{!following && !watching ? (
<button className="following-option a-dropdown__item-inner is-active">
<div className="following-option__inner">
<div className="following-option__label">フォローしない</div>
</div>
</button>
) : (
<button
className="following-option a-dropdown__item-inner"
onClick={unfollow}>
<div className="following-option__inner">
<div className="following-option__label">フォローしない</div>
</div>
</button>
)}
</li>
</ul>
</div>
</details>
)
}
64 changes: 64 additions & 0 deletions app/javascript/components/GenerationUsers.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react'
import useSWR from 'swr'
import User from './User.jsx'
import Pagination from './Pagination'
import usePage from './hooks/usePage'
import fetcher from '../fetcher'

export default function GenerationUsers({ generationID }) {
const itemsPerPage = 24
const { page, setPage } = usePage()
const { data, error } = useSWR(
`/api/generations/${generationID}.json?page=${page}&per=${itemsPerPage}`,
fetcher
)

if (error) return <>An error has occurred.</>
if (!data) return <>Loading...</>

return (
<div className="page-body">
{data.totalPages > 1 && (
<Pagination
sum={data.totalPages * itemsPerPage}
per={itemsPerPage}
page={page}
setPage={setPage}
/>
)}
<div className="container">
<div className="users row">
{data.users === null ? (
<div className="empty">
<i className="fa-solid fa-spinner fa-pulse" />
ロード中
</div>
) : data.users.length !== 0 ? (
data.users.map((user) => (
<User key={user.id} user={user} currentUser={data.currentUser} />
))
) : (
<div className="row">
<div className="o-empty-message">
<div className="o-empty-message__icon">
<i className="fa-regular fa-smile"></i>
</div>
<p className="o-empty-message_text">
{generationID}期のユーザー一覧はありません
</p>
</div>
</div>
)}
</div>
</div>
{data.totalPages > 1 && (
<Pagination
sum={data.totalPages * itemsPerPage}
per={itemsPerPage}
page={page}
setPage={setPage}
/>
)}
</div>
)
}
168 changes: 168 additions & 0 deletions app/javascript/components/User.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React from 'react'
import Following from './Following.jsx'
import UserActivityCounts from './UserActivityCounts.jsx'
import UserSns from './UserSns.jsx'
import UserTags from './UserTags.jsx'
import UserPracticeProgress from './UserPracticeProgress.jsx'

export default function User({ user, currentUser }) {
const userDescParagraphs = () => {
let description = user.description
description =
description.length <= 200
? description
: description.substring(0, 200) + '...'
const paragraphs = description.split(/\n|\r\n/).map((text, i) => {
return {
id: i,
text: text
}
})
return paragraphs
}

const roleClass = () => `is-${user.primary_role}`

return (
<div className="col-xxl-3 col-xl-4 col-lg-4 col-md-6 col-xs-12">
<div className="users-item">
<div className={`users-item__inner a-card ${roleClass()}`}>
{currentUser &&
(currentUser.mentor || currentUser.admin) &&
user.student_or_trainee && (
<div className="users-item__inactive-message-container is-only-mentor">
{user.roles.includes('retired') && (
<div className="users-item__inactive-message">
退会しました
</div>
)}
{user.roles.includes('hibernationed') && (
<div className="users-item__inactive-message">
休会中: {user.hibernated_at}〜(
{user.hibernation_elapsed_days}日経過)
</div>
)}
{!user.active && (
<div className="users-item__inactive-message">
1ヶ月以上ログインがありません
</div>
)}
</div>
)}
<header className="users-item__header">
<div className="users-item__header-inner">
<div className="users-item__header-start">
<div className="users-item__icon">
<a href={user.url}>
<span className={`a-user-role ${roleClass()}`}>
<img
className="users-item__user-icon-image a-user-icon"
title={user.icon_title}
alt={user.icon_title}
src={user.avatar_url}
/>
</span>
</a>
</div>
</div>
<div className="users-item__header-end">
<div className="card-list-item__rows">
<div className="card-list-item__row">
<div className="card-list-item-title">
<a
className="card-list-item-title__title is-lg a-text-link"
href={user.url}>
{user.login_name}
</a>
{user.company && user.company.logo_url && (
<a href={user.company.url}>
<img
className="user-item__company-logo"
src={user.company.logo_url}
/>
</a>
)}
</div>
</div>
<div className="card-list-item__row">
<div className="card-list-item-meta">
<div className="card-list-item-meta__items">
<div className="card-list-item-meta__item">
<div className="a-meta">{user.name}</div>
</div>
<div className="card-list-item-meta__item">
{user.discord_profile.times_url ? (
<a
className="a-meta"
href={user.discord_profile.times_url}>
<i className="fa-brands fa-discord"></i>
{user.discord_profile.account_name}
</a>
) : (
<div className="a-meta">
<i className="fa-brands fa-discord"></i>
{user.discord_profile.account_name}
</div>
)}
</div>
</div>
</div>
</div>
</div>
<UserSns user={user} />
</div>
</div>
<UserActivityCounts user={user} />
</header>
<div className="users-item__body">
<div className="users-item__description a-short-text">
{userDescParagraphs().map((paragraph) => (
<p key={paragraph.id}>{paragraph.text}</p>
))}
</div>
<div className="users-item__tags">
<UserTags user={user} />
</div>
</div>
<UserPracticeProgress user={user} />
<hr className="a-border-tint" />
<footer className="card-footer users-item__footer">
<div className="card-main-actions">
<ul className="card-main-actions__items">
{currentUser.id !== user.id &&
currentUser.adviser &&
user.company &&
currentUser.company_id === user.company.id && (
<li className="card-main-actions__item">
<div className="a-button is-disabled is-sm is-block">
<i className="fa-solid fa-check"></i>
<span>自社研修生</span>
</div>
</li>
)}
{currentUser.id !== user.id && (
<li className="card-main-actions__item">
<Following
isFollowing={user.isFollowing}
userId={user.id}
isWatching={user.isWatching}
/>
</li>
)}
{currentUser.admin && user.talkUrl && (
<li className="card-main-actions__item is-only-mentor">
<a
className="a-button is-secondary is-sm is-block"
href={user.talkUrl}>
相談部屋
</a>
</li>
)}
</ul>
</div>
</footer>
</div>
</div>
</div>
)
}
Loading

0 comments on commit e6f3de4

Please sign in to comment.