-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7244 from fjordllc/main
Release 2024-01-31 14:27:39
- Loading branch information
Showing
45 changed files
with
798 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# frozen_string_literal: true | ||
|
||
module UserDecorator | ||
module Retire | ||
def retire_countdown | ||
ActiveSupport::Duration.build(scheduled_retire_at - Time.current) if hibernated_at? | ||
end | ||
|
||
def retire_deadline | ||
countdown = | ||
if retire_countdown.in_hours < 1 | ||
"#{retire_countdown.in_minutes.floor}分" | ||
elsif retire_countdown.in_hours < 24 | ||
"#{retire_countdown.in_hours.floor}時間" | ||
else | ||
"#{retire_countdown.in_days.floor}日" | ||
end | ||
|
||
"#{l scheduled_retire_at} (自動退会まであと#{countdown})" | ||
end | ||
|
||
def countdown_danger_tag | ||
retire_countdown.in_days <= 7 ? 'is-danger' : '' | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# frozen_string_literal: true | ||
|
||
module UserDecorator | ||
module Role | ||
def roles | ||
role_list = [ | ||
{ role: 'retired', value: retired? }, | ||
{ role: 'hibernationed', value: hibernated? }, | ||
{ role: 'admin', value: admin? }, | ||
{ role: 'mentor', value: mentor? }, | ||
{ role: 'adviser', value: adviser? }, | ||
{ role: 'graduate', value: graduated? }, | ||
{ role: 'trainee', value: trainee? } | ||
] | ||
roles = role_list.find_all { |v| v[:value] } | ||
.map { |v| v[:role] } | ||
roles << :student if roles.empty? | ||
|
||
roles | ||
end | ||
|
||
def primary_role | ||
roles.first | ||
end | ||
|
||
def staff_roles | ||
staff_roles = [ | ||
{ role: '管理者', value: admin? }, | ||
{ role: 'メンター', value: mentor? }, | ||
{ role: 'アドバイザー', value: adviser? } | ||
] | ||
staff_roles.find_all { |v| v[:value] } | ||
.map { |v| v[:role] } | ||
.join('、') | ||
end | ||
|
||
def roles_to_s | ||
return '' if roles.empty? | ||
|
||
roles = [ | ||
{ role: '退会ユーザー', value: retired? }, | ||
{ role: '休会ユーザー', value: hibernated? }, | ||
{ role: '管理者', value: admin? }, | ||
{ role: 'メンター', value: mentor? }, | ||
{ role: 'アドバイザー', value: adviser? }, | ||
{ role: '卒業生', value: graduated? }, | ||
{ role: '研修生', value: trainee? } | ||
] | ||
roles.find_all { |v| v[:value] } | ||
.map { |v| v[:role] } | ||
.join('、') | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import React from 'react' | ||
import dayjs from 'dayjs' | ||
import ja from 'dayjs/locale/ja' | ||
dayjs.locale(ja) | ||
|
||
export default function Searchable({ searchable, word }) { | ||
const roleClass = `is-${searchable.primary_role}` | ||
const searchableClass = searchable.wip | ||
? `is-wip is-${searchable.model_name}` | ||
: `is-${searchable.model_name}` | ||
|
||
const userUrl = `/users/${searchable.user_id}` | ||
const documentAuthorUserUrl = `/users/${searchable.document_author_id}` | ||
const talkUrl = `/talks/${searchable.talk_id}` | ||
|
||
const updatedAt = dayjs(searchable.updated_at).format( | ||
'YYYY年MM月DD日(dd) HH:mm' | ||
) | ||
const canDisplayTalk = searchable.model_name === 'user' && searchable.talk_id | ||
const currentUser = window.currentUser | ||
|
||
const labelContent = | ||
searchable.model_name === 'regular_event' ? ( | ||
<span className="card-list-item__label-inner is-sm"> | ||
定期 | ||
<br /> | ||
イベント | ||
</span> | ||
) : searchable.model_name === 'event' ? ( | ||
<span className="card-list-item__label-inner is-sm"> | ||
特別 | ||
<br /> | ||
イベント | ||
</span> | ||
) : searchable.model_name === 'practice' ? ( | ||
<span className="card-list-item__label-inner"> | ||
プラク | ||
<br /> | ||
ティス | ||
</span> | ||
) : ( | ||
<span className="card-list-item__label"> | ||
{searchable.model_name_with_i18n} | ||
</span> | ||
) | ||
|
||
const badgeContent = searchable.wip ? ( | ||
<div className="a-list-item-badge is-wip"> | ||
<span>WIP</span> | ||
</div> | ||
) : searchable.is_comment_or_answer ? ( | ||
<div className="a-list-item-badge is-searchable"> | ||
<span>コメント</span> | ||
</div> | ||
) : searchable.is_user ? ( | ||
<div className="a-list-item-badge is-searchable"> | ||
<span>ユーザー</span> | ||
</div> | ||
) : ( | ||
'' | ||
) | ||
|
||
const summary = () => { | ||
const wordsPattern = word | ||
.trim() | ||
.replaceAll(/[.*+?^=!:${}()|[\]/\\]/g, '\\$&') | ||
.replaceAll(/\s+/g, '|') | ||
const pattern = new RegExp(wordsPattern, 'gi') | ||
if (word) { | ||
return searchable.summary.replaceAll( | ||
pattern, | ||
`<strong class='matched_word'>$&</strong>` | ||
) | ||
} else { | ||
return searchable.summary | ||
} | ||
} | ||
|
||
return ( | ||
<div className={`card-list-item ${searchableClass}`}> | ||
<div className="card-list-item__inner"> | ||
{searchable.is_user && ( | ||
<div className="card-list-item__user"> | ||
<a className="card-list-item__user-link" href={searchable.url}> | ||
<span className={`a-user-role ${roleClass}`}> | ||
<img | ||
className="card-list-item__user-icon a-user-icon" | ||
src={searchable.avatar_url} | ||
title={searchable.title} | ||
alt={searchable.title}></img> | ||
</span> | ||
</a> | ||
</div> | ||
)} | ||
{!searchable.is_user && ( | ||
<div className="card-list-item__label">{labelContent}</div> | ||
)} | ||
<div className="card-list-item__rows"> | ||
<div className="card-list-item__row"> | ||
<div className="card-list-item-title"> | ||
{badgeContent} | ||
<div className="card-list-item-title__title"> | ||
<a | ||
className="card-list-item-title__link a-text-link" | ||
href={searchable.url}> | ||
{searchable.title} | ||
</a> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="card-list-item__row"> | ||
<div className="card-list-item__summary"> | ||
<p dangerouslySetInnerHTML={{ __html: summary() }}></p> | ||
</div> | ||
</div> | ||
<div className="card-list-item__row"> | ||
<div className="card-list-item-meta"> | ||
<div className="card-list-item-meta__items"> | ||
{!['practice', 'page', 'user'].includes( | ||
searchable.model_name | ||
) && ( | ||
<div className="card-list-item-meta__item"> | ||
<div className="card-list-item-meta__user"> | ||
<a | ||
className="card-list-item-meta__icon-link" | ||
href={userUrl}> | ||
<span className={`a-user-role ${roleClass}`}> | ||
<img | ||
className="card-list-item-meta__icon a-user-icon" | ||
src={searchable.avatar_url} | ||
title={searchable.icon_title} | ||
alt={searchable.icon_title}></img> | ||
</span> | ||
</a> | ||
<a className="a-user-name" href={userUrl}> | ||
{searchable.login_name} | ||
</a> | ||
</div> | ||
</div> | ||
)} | ||
<div className="card-list-item-meta__item"> | ||
<div | ||
className={`time a-meta datetime=${searchable.updated_at} pubdate="pubdate"`}> | ||
{updatedAt} | ||
</div> | ||
</div> | ||
{searchable.is_comment_or_answer && ( | ||
<div className="card-list-item-meta__item"> | ||
<div className="a-meta"> | ||
{'('} | ||
<a className="a-user-name" href={documentAuthorUserUrl}> | ||
{searchable.document_author_login_name} | ||
</a>{' '} | ||
{searchable.model_name_with_i18n} | ||
{')'} | ||
</div> | ||
</div> | ||
)} | ||
{currentUser.roles.includes('admin') && canDisplayTalk && ( | ||
<div className="card-list-item-meta__item"> | ||
<a className="a-text-link" href={talkUrl}> | ||
相談部屋 | ||
</a> | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.