refactor learning dashboard to use Reactions instead of Emojis
This commit is contained in:
parent
09f565d0ef
commit
736c9a130f
@ -41,8 +41,9 @@ case class User(
|
||||
answers: Map[String,Vector[String]] = Map(),
|
||||
genericData: Map[String, Vector[GenericData]] = Map(),
|
||||
talk: Talk = Talk(),
|
||||
emojis: Vector[Emoji] = Vector(),
|
||||
reactions: Vector[Emoji] = Vector(),
|
||||
reactions: Vector[Reaction] = Vector(),
|
||||
raiseHand: Vector[Long] = Vector(),
|
||||
away: Vector[Away] = Vector(),
|
||||
webcams: Vector[Webcam] = Vector(),
|
||||
totalOfMessages: Long = 0,
|
||||
)
|
||||
@ -75,11 +76,16 @@ case class Talk(
|
||||
lastTalkStartedOn: Long = 0,
|
||||
)
|
||||
|
||||
case class Emoji(
|
||||
case class Reaction(
|
||||
name: String,
|
||||
sentOn: Long = System.currentTimeMillis(),
|
||||
)
|
||||
|
||||
case class Away(
|
||||
startedOn: Long = System.currentTimeMillis(),
|
||||
stoppedOn: Long = 0,
|
||||
)
|
||||
|
||||
case class Webcam(
|
||||
startedOn: Long = System.currentTimeMillis(),
|
||||
stoppedOn: Long = 0,
|
||||
@ -366,7 +372,7 @@ class LearningDashboardActor(
|
||||
user <- findUserByIntId(meeting, msg.body.userId)
|
||||
} yield {
|
||||
if (msg.body.raiseHand) {
|
||||
val updatedUser = user.copy(emojis = user.emojis :+ Emoji("raiseHand"))
|
||||
val updatedUser = user.copy(raiseHand = user.raiseHand :+ System.currentTimeMillis())
|
||||
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
|
||||
|
||||
meetings += (updatedMeeting.intId -> updatedMeeting)
|
||||
@ -379,12 +385,15 @@ class LearningDashboardActor(
|
||||
meeting <- meetings.values.find(m => m.intId == msg.header.meetingId)
|
||||
user <- findUserByIntId(meeting, msg.body.userId)
|
||||
} yield {
|
||||
if (msg.body.away) {
|
||||
val updatedUser = user.copy(emojis = user.emojis :+ Emoji("away"))
|
||||
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
|
||||
|
||||
meetings += (updatedMeeting.intId -> updatedMeeting)
|
||||
val updatedUser = if (msg.body.away) {
|
||||
user.copy(away = user.away :+ Away())
|
||||
} else {
|
||||
val lastAway: Away = user.away.last.copy(stoppedOn = System.currentTimeMillis())
|
||||
user.copy(away = user.away.dropRight(1) :+ lastAway)
|
||||
}
|
||||
|
||||
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
|
||||
meetings += (updatedMeeting.intId -> updatedMeeting)
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,26 +409,9 @@ class LearningDashboardActor(
|
||||
}).length > 0
|
||||
|
||||
if(!hasSameReactionInLast30Seconds) {
|
||||
val updatedUser = user.copy(reactions = user.reactions :+ Emoji(msg.body.reactionEmoji))
|
||||
val updatedUser = user.copy(reactions = user.reactions :+ Reaction(msg.body.reactionEmoji))
|
||||
val updatedMeeting = meeting.copy(users = meeting.users + (updatedUser.userKey -> updatedUser))
|
||||
meetings += (updatedMeeting.intId -> updatedMeeting)
|
||||
|
||||
//Convert Reactions to legacy Emoji (while LearningDashboard doesn't support Reactions)
|
||||
val emoji = msg.body.reactionEmoji.codePointAt(0) match {
|
||||
case 128515 => "happy"
|
||||
case 128528 => "neutral"
|
||||
case 128577 => "sad"
|
||||
case 128077 => "thumbsUp"
|
||||
case 128078 => "thumbsDown"
|
||||
case 128079 => "applause"
|
||||
case _ => "none"
|
||||
}
|
||||
|
||||
if (emoji != "none") {
|
||||
val updatedUserWithEmoji = updatedUser.copy(emojis = user.emojis :+ Emoji(emoji))
|
||||
val updatedMeetingWithEmoji = meeting.copy(users = meeting.users + (updatedUserWithEmoji.userKey -> updatedUserWithEmoji))
|
||||
meetings += (updatedMeeting.intId -> updatedMeetingWithEmoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ module.exports = {
|
||||
'import/no-absolute-path': 0,
|
||||
'import/no-unresolved': 0,
|
||||
'import/no-extraneous-dependencies': 1,
|
||||
'import/no-named-as-default': 0,
|
||||
'import/no-named-as-default-member': 0,
|
||||
'react/prop-types': 'off',
|
||||
'jsx-a11y/no-access-key': 0,
|
||||
'react/jsx-props-no-spreading': 'off',
|
||||
|
@ -10,7 +10,6 @@ import './bbb-icons.css';
|
||||
import {
|
||||
FormattedMessage, FormattedDate, injectIntl, FormattedTime,
|
||||
} from 'react-intl';
|
||||
import { emojiConfigs } from './services/EmojiService';
|
||||
import CardBody from './components/Card';
|
||||
import UsersTable from './components/UsersTable';
|
||||
import UserDetails from './components/UserDetails/component';
|
||||
@ -125,38 +124,31 @@ class App extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
fetchMostUsedEmojis() {
|
||||
fetchMostUsedReactions() {
|
||||
const { activitiesJson } = this.state;
|
||||
if (!activitiesJson) { return []; }
|
||||
|
||||
// Icon elements
|
||||
const emojis = [...Object.keys(emojiConfigs)];
|
||||
const icons = {};
|
||||
emojis.forEach((emoji) => {
|
||||
icons[emoji] = (<i className={`${emojiConfigs[emoji].icon} bbb-icon-card`} />);
|
||||
});
|
||||
|
||||
// Count each emoji
|
||||
const emojiCount = {};
|
||||
emojis.forEach((emoji) => {
|
||||
emojiCount[emoji] = 0;
|
||||
});
|
||||
const allEmojisUsed = Object
|
||||
// Count each reaction
|
||||
const reactionCount = {};
|
||||
const allReactionsUsed = Object
|
||||
.values(activitiesJson.users || {})
|
||||
.map((user) => user.emojis || [])
|
||||
.map((user) => user.reactions || [])
|
||||
.flat(1);
|
||||
allEmojisUsed.forEach((emoji) => {
|
||||
emojiCount[emoji.name] += 1;
|
||||
allReactionsUsed.forEach((reaction) => {
|
||||
if (typeof reactionCount[reaction.name] === 'undefined') {
|
||||
reactionCount[reaction.name] = 0;
|
||||
}
|
||||
reactionCount[reaction.name] += 1;
|
||||
});
|
||||
|
||||
// Get the three most used
|
||||
const mostUsedEmojis = Object
|
||||
.entries(emojiCount)
|
||||
const mostUsedReactions = Object
|
||||
.entries(reactionCount)
|
||||
.filter(([, count]) => count)
|
||||
.sort(([, countA], [, countB]) => countA - countB)
|
||||
.reverse()
|
||||
.slice(0, 3);
|
||||
return mostUsedEmojis.map(([emoji]) => icons[emoji]);
|
||||
return mostUsedReactions.map(([reaction]) => reaction);
|
||||
}
|
||||
|
||||
updateModalUser() {
|
||||
@ -245,14 +237,13 @@ class App extends React.Component {
|
||||
) => genericDataListForSpecificUser?.columnTitle);
|
||||
// This line will eliminate duplicates.
|
||||
const genericDataColumnTitleList = [...new Set(genericDataColumnTitleWithDuplicates)];
|
||||
console.log('teste aqui ----> ', activitiesJson, genericDataColumnTitleList);
|
||||
|
||||
document.title = `${intl.formatMessage({ id: 'app.learningDashboard.bigbluebuttonTitle', defaultMessage: 'BigBlueButton' })} - ${intl.formatMessage({ id: 'app.learningDashboard.dashboardTitle', defaultMessage: 'Learning Analytics Dashboard' })} - ${activitiesJson.name}`;
|
||||
|
||||
function totalOfEmojis() {
|
||||
function totalOfReactions() {
|
||||
if (activitiesJson && activitiesJson.users) {
|
||||
return Object.values(activitiesJson.users)
|
||||
.reduce((prevVal, elem) => prevVal + elem.emojis.length, 0);
|
||||
.reduce((prevVal, elem) => prevVal + elem.reactions.length, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -303,19 +294,19 @@ class App extends React.Component {
|
||||
}
|
||||
|
||||
// Calculate points of Raise hand
|
||||
const usersRaiseHand = allUsers.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
|
||||
const usersRaiseHand = allUsers.map((currUser) => currUser.raiseHand.length);
|
||||
const maxRaiseHand = Math.max(...usersRaiseHand);
|
||||
const totalRaiseHand = usersRaiseHand.reduce((prev, val) => prev + val, 0);
|
||||
if (maxRaiseHand > 0) {
|
||||
meetingAveragePoints += ((totalRaiseHand / nrOfUsers) / maxRaiseHand) * 2;
|
||||
}
|
||||
|
||||
// Calculate points of Emojis
|
||||
const usersEmojis = allUsers.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
|
||||
const maxEmojis = Math.max(...usersEmojis);
|
||||
const totalEmojis = usersEmojis.reduce((prev, val) => prev + val, 0);
|
||||
if (maxEmojis > 0) {
|
||||
meetingAveragePoints += ((totalEmojis / nrOfUsers) / maxEmojis) * 2;
|
||||
// Calculate points of Reactions
|
||||
const usersReactions = allUsers.map((currUser) => currUser.reactions.length);
|
||||
const maxReactions = Math.max(...usersReactions);
|
||||
const totalReactions = usersReactions.reduce((prev, val) => prev + val, 0);
|
||||
if (maxReactions > 0) {
|
||||
meetingAveragePoints += ((totalReactions / nrOfUsers) / maxReactions) * 2;
|
||||
}
|
||||
|
||||
// Calculate points of Polls
|
||||
@ -482,11 +473,11 @@ class App extends React.Component {
|
||||
<CardContent classes={{ root: '!p-0' }}>
|
||||
<CardBody
|
||||
name={intl.formatMessage({ id: 'app.learningDashboard.indicators.timeline', defaultMessage: 'Timeline' })}
|
||||
number={totalOfEmojis()}
|
||||
number={totalOfReactions()}
|
||||
cardClass={tab === TABS.TIMELINE ? 'border-purple-500' : 'hover:border-purple-500 border-white'}
|
||||
iconClass="bg-purple-200 text-purple-500"
|
||||
>
|
||||
{this.fetchMostUsedEmojis()}
|
||||
{this.fetchMostUsedReactions()}
|
||||
</CardBody>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { emojiConfigs, filterUserEmojis } from '../services/EmojiService';
|
||||
import { filterUserReactions } from '../services/ReactionService';
|
||||
import UserAvatar from './UserAvatar';
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
@ -24,31 +24,31 @@ const intlMessages = defineMessages({
|
||||
|
||||
class StatusTable extends React.Component {
|
||||
componentDidMount() {
|
||||
// This code is needed to prevent emojis from overflowing.
|
||||
const emojis = document.getElementsByClassName('timeline-emoji');
|
||||
for (let i = 0; i < emojis.length; i += 1) {
|
||||
const emojiStyle = window.getComputedStyle(emojis[i]);
|
||||
const offsetLeft = Number(emojiStyle
|
||||
// This code is needed to prevent reactions from overflowing.
|
||||
const reactions = document.getElementsByClassName('timeline-reaction');
|
||||
for (let i = 0; i < reactions.length; i += 1) {
|
||||
const reactionStyle = window.getComputedStyle(reactions[i]);
|
||||
const offsetLeft = Number(reactionStyle
|
||||
.left
|
||||
.replace(/px/g, '')
|
||||
.trim());
|
||||
if (offsetLeft < 0) {
|
||||
emojis[i].style.left = '0';
|
||||
reactions[i].style.left = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// This code is needed to prevent emojis from overflowing.
|
||||
const emojis = document.getElementsByClassName('timeline-emoji');
|
||||
for (let i = 0; i < emojis.length; i += 1) {
|
||||
const emojiStyle = window.getComputedStyle(emojis[i]);
|
||||
const offsetLeft = Number(emojiStyle
|
||||
// This code is needed to prevent reactions from overflowing.
|
||||
const reactions = document.getElementsByClassName('timeline-reaction');
|
||||
for (let i = 0; i < reactions.length; i += 1) {
|
||||
const reactionStyle = window.getComputedStyle(reactions[i]);
|
||||
const offsetLeft = Number(reactionStyle
|
||||
.left
|
||||
.replace(/px/g, '')
|
||||
.trim());
|
||||
if (offsetLeft < 0) {
|
||||
emojis[i].style.left = '0';
|
||||
reactions[i].style.left = '0';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,8 +287,7 @@ class StatusTable extends React.Component {
|
||||
{ usersPeriods[user.userKey].length > 0 ? (
|
||||
usersPeriods[user.userKey].map((userPeriod) => {
|
||||
const { registeredOn, leftOn } = userPeriod;
|
||||
const userEmojisInPeriod = filterUserEmojis(user,
|
||||
null,
|
||||
const userReactionsInPeriod = filterUserReactions(user,
|
||||
registeredOn >= boundaryLeft && registeredOn <= boundaryRight
|
||||
? registeredOn : boundaryLeft,
|
||||
leftOn >= boundaryLeft && leftOn <= boundaryRight
|
||||
@ -301,34 +300,21 @@ class StatusTable extends React.Component {
|
||||
|| (boundaryLeft >= registeredOn && leftOn === 0)
|
||||
? makeLineThrough(userPeriod, period)
|
||||
: null }
|
||||
{ userEmojisInPeriod.map((emoji) => {
|
||||
const offset = ((emoji.sentOn - period.start) * 100)
|
||||
{ userReactionsInPeriod.map((reaction) => {
|
||||
const offset = ((reaction.sentOn - period.start) * 100)
|
||||
/ (interval);
|
||||
const origin = isRTL ? 'right' : 'left';
|
||||
const redress = '(0.875rem / 2 + 0.25rem + 2px)';
|
||||
return (
|
||||
<div
|
||||
className="flex absolute p-1 border-white border-2 rounded-full text-sm z-20 bg-purple-500 text-purple-200 timeline-emoji"
|
||||
className="flex absolute p-1 border-white border-2 rounded-full text-sm z-20 bg-purple-500 text-purple-200 timeline-reaction select-none"
|
||||
role="generic"
|
||||
aria-label={intl.formatMessage({
|
||||
id: emojiConfigs[emoji.name].intlId,
|
||||
defaultMessage: emojiConfigs[emoji.name].defaultMessage,
|
||||
})}
|
||||
style={{
|
||||
top: `calc(50% - ${redress})`,
|
||||
[origin]: `calc(${offset}% - ${redress})`,
|
||||
}}
|
||||
title={intl.formatMessage({
|
||||
id: emojiConfigs[emoji.name].intlId,
|
||||
defaultMessage: emojiConfigs[emoji.name].defaultMessage,
|
||||
})}
|
||||
>
|
||||
<i
|
||||
className={
|
||||
'text-sm bbb-icon-timeline'
|
||||
+ ` ${emojiConfigs[emoji.name].icon}`
|
||||
}
|
||||
/>
|
||||
{reaction.name}
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
|
@ -109,8 +109,8 @@ const UserDatailsComponent = (props) => {
|
||||
|
||||
const usersTalkTime = allUsersArr.map((currUser) => currUser.talk.totalTime);
|
||||
const usersMessages = allUsersArr.map((currUser) => currUser.totalOfMessages);
|
||||
const usersEmojis = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
|
||||
const usersRaiseHands = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
|
||||
const usersReactions = allUsersArr.map((currUser) => currUser.reactions.length);
|
||||
const usersRaiseHands = allUsersArr.map((currUser) => currUser.raiseHand.length);
|
||||
const usersAnswers = allUsersArr.map((currUser) => Object.values(currUser.answers || {}).length);
|
||||
const totalPolls = Object.values(polls || {}).length;
|
||||
|
||||
@ -132,18 +132,18 @@ const UserDatailsComponent = (props) => {
|
||||
|
||||
function getPointsOfRaiseHand(u) {
|
||||
const maxRaiseHand = Math.max(...usersRaiseHands);
|
||||
const userRaiseHand = u.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
|
||||
const userRaiseHand = u.reactions.length;
|
||||
if (maxRaiseHand > 0) {
|
||||
return (userRaiseHand / maxRaiseHand) * 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getPointsofEmoji(u) {
|
||||
const maxEmojis = Math.max(...usersEmojis);
|
||||
const userEmojis = u.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
|
||||
if (maxEmojis > 0) {
|
||||
return (userEmojis / maxEmojis) * 2;
|
||||
function getPointsofReaction(u) {
|
||||
const maxReactions = Math.max(...usersReactions);
|
||||
const userReactions = u.reactions.length;
|
||||
if (maxReactions > 0) {
|
||||
return (userReactions / maxReactions) * 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -161,7 +161,7 @@ const UserDatailsComponent = (props) => {
|
||||
const messagesAverage = usersMessages
|
||||
.reduce((prev, curr) => prev + curr, 0) / (allUsersArr.length || 1);
|
||||
|
||||
const emojisAverage = usersEmojis
|
||||
const reactionsAverage = usersReactions
|
||||
.reduce((prev, curr) => prev + curr, 0) / (allUsersArr.length || 1);
|
||||
|
||||
const raiseHandsAverage = usersRaiseHands
|
||||
@ -173,7 +173,7 @@ const UserDatailsComponent = (props) => {
|
||||
const activityPointsFunctions = {
|
||||
'Talk Time': getPointsOfTalk,
|
||||
Messages: getPointsOfChatting,
|
||||
Emojis: getPointsofEmoji,
|
||||
Reactions: getPointsofReaction,
|
||||
'Raise Hands': getPointsOfRaiseHand,
|
||||
'Poll Votes': getPointsOfPolls,
|
||||
};
|
||||
@ -181,7 +181,7 @@ const UserDatailsComponent = (props) => {
|
||||
const averages = {
|
||||
'Talk Time': talkTimeAverage,
|
||||
Messages: messagesAverage,
|
||||
Emojis: emojisAverage,
|
||||
Reactions: reactionsAverage,
|
||||
'Raise Hands': raiseHandsAverage,
|
||||
'Poll Votes': pollsAverage,
|
||||
};
|
||||
@ -436,7 +436,7 @@ const UserDatailsComponent = (props) => {
|
||||
<th aria-label="Average" className="grow text-center font-normal"><FormattedMessage id="app.learningDashboard.userDetails.average" defaultMessage="Average" /></th>
|
||||
<th aria-label="Activity Points" className="min-w-[20%] text-ellipsis text-right rtl:text-left font-normal"><FormattedMessage id="app.learningDashboard.userDetails.activityPoints" defaultMessage="Activity Points" /></th>
|
||||
</tr>
|
||||
{ ['Talk Time', 'Messages', 'Emojis', 'Raise Hands', 'Poll Votes'].map((category) => {
|
||||
{ ['Talk Time', 'Messages', 'Reactions', 'Raise Hands', 'Poll Votes'].map((category) => {
|
||||
let totalOfActivity = 0;
|
||||
|
||||
switch (category) {
|
||||
@ -446,11 +446,11 @@ const UserDatailsComponent = (props) => {
|
||||
case 'Messages':
|
||||
totalOfActivity = user.totalOfMessages;
|
||||
break;
|
||||
case 'Emojis':
|
||||
totalOfActivity = user.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
|
||||
case 'Reactions':
|
||||
totalOfActivity = user.reactions.length;
|
||||
break;
|
||||
case 'Raise Hands':
|
||||
totalOfActivity = user.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
|
||||
totalOfActivity = user.raiseHand.length;
|
||||
break;
|
||||
case 'Poll Votes':
|
||||
totalOfActivity = Object.values(user.answers).length;
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import {
|
||||
FormattedMessage, FormattedDate, FormattedNumber, injectIntl,
|
||||
} from 'react-intl';
|
||||
import { getUserEmojisSummary, emojiConfigs } from '../services/EmojiService';
|
||||
import { getUserReactionsSummary } from '../services/ReactionService';
|
||||
import { getActivityScore, getSumOfTime, tsToHHmmss } from '../services/UserService';
|
||||
import UserAvatar from './UserAvatar';
|
||||
import { UserDetailsContext } from './UserDetails/context';
|
||||
@ -73,9 +73,9 @@ class UsersTable extends React.Component {
|
||||
talkTimeOrder, webcamTimeOrder, lastFieldClicked,
|
||||
} = this.state;
|
||||
|
||||
const usersEmojisSummary = {};
|
||||
const usersReactionsSummary = {};
|
||||
Object.values(allUsers || {}).forEach((user) => {
|
||||
usersEmojisSummary[user.userKey] = getUserEmojisSummary(user, 'raiseHand');
|
||||
usersReactionsSummary[user.userKey] = getUserReactionsSummary(user);
|
||||
});
|
||||
|
||||
function getOnlinePercentage(registeredOn, leftOn) {
|
||||
@ -206,7 +206,7 @@ class UsersTable extends React.Component {
|
||||
<FormattedMessage id="app.learningDashboard.usersTable.colMessages" defaultMessage="Messages" />
|
||||
</th>
|
||||
<th className="px-3.5 2xl:px-4 py-3 col-text-left">
|
||||
<FormattedMessage id="app.learningDashboard.usersTable.colEmojis" defaultMessage="Emojis" />
|
||||
<FormattedMessage id="app.learningDashboard.usersTable.colReactions" defaultMessage="Reactions" />
|
||||
</th>
|
||||
<th className="px-3.5 2xl:px-4 py-3 text-center">
|
||||
<FormattedMessage id="app.learningDashboard.usersTable.colRaiseHands" defaultMessage="Raise Hand" />
|
||||
@ -429,42 +429,25 @@ class UsersTable extends React.Component {
|
||||
</span>
|
||||
) : null }
|
||||
</td>
|
||||
<td className={`px-4 py-3 text-sm col-text-left ${opacity}`} data-test="userTotalEmojisDashboard">
|
||||
<td className={`px-4 py-3 text-sm col-text-left ${opacity}`} data-test="userTotalReactionsDashboard">
|
||||
{
|
||||
Object.keys(usersEmojisSummary[user.userKey] || {}).map((emoji) => (
|
||||
Object.keys(usersReactionsSummary[user.userKey] || {}).map((reaction) => (
|
||||
<div className="text-xs whitespace-nowrap">
|
||||
<i className={`${emojiConfigs[emoji].icon} text-sm`} />
|
||||
{reaction}
|
||||
|
||||
{ usersEmojisSummary[user.userKey][emoji] }
|
||||
{ usersReactionsSummary[user.userKey][reaction] }
|
||||
|
||||
<FormattedMessage
|
||||
id={emojiConfigs[emoji].intlId}
|
||||
defaultMessage={emojiConfigs[emoji].defaultMessage}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</td>
|
||||
<td className={`px-4 py-3 text-sm text-center ${opacity}`} data-test="userRaiseHandDashboard">
|
||||
{ user.emojis.filter((emoji) => emoji.name === 'raiseHand').length > 0
|
||||
{ user.raiseHand.length > 0
|
||||
? (
|
||||
<span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4 inline"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M7 11.5V14m0-2.5v-6a1.5 1.5 0 113 0m-3 6a1.5 1.5 0 00-3 0v2a7.5 7.5 0 0015 0v-5a1.5 1.5 0 00-3 0m-6-3V11m0-5.5v-1a1.5 1.5 0 013 0v1m0 0V11m0-5.5a1.5 1.5 0 013 0v3m0 0V11"
|
||||
/>
|
||||
</svg>
|
||||
✋
|
||||
|
||||
{user.emojis.filter((emoji) => emoji.name === 'raiseHand').length}
|
||||
{user.raiseHand.length}
|
||||
</span>
|
||||
) : null }
|
||||
</td>
|
||||
|
@ -1,74 +0,0 @@
|
||||
export const emojiConfigs = {
|
||||
away: {
|
||||
icon: 'icon-bbb-time',
|
||||
intlId: 'app.actionsBar.emojiMenu.awayLabel',
|
||||
defaultMessage: 'Away',
|
||||
},
|
||||
neutral: {
|
||||
icon: 'icon-bbb-undecided',
|
||||
intlId: 'app.actionsBar.emojiMenu.neutralLabel',
|
||||
defaultMessage: 'Undecided',
|
||||
},
|
||||
confused: {
|
||||
icon: 'icon-bbb-confused',
|
||||
intlId: 'app.actionsBar.emojiMenu.confusedLabel',
|
||||
defaultMessage: 'Confused',
|
||||
},
|
||||
sad: {
|
||||
icon: 'icon-bbb-sad',
|
||||
intlId: 'app.actionsBar.emojiMenu.sadLabel',
|
||||
defaultMessage: 'Sad',
|
||||
},
|
||||
happy: {
|
||||
icon: 'icon-bbb-happy',
|
||||
intlId: 'app.actionsBar.emojiMenu.happyLabel',
|
||||
defaultMessage: 'Happy',
|
||||
},
|
||||
applause: {
|
||||
icon: 'icon-bbb-applause',
|
||||
intlId: 'app.actionsBar.emojiMenu.applauseLabel',
|
||||
defaultMessage: 'Applaud',
|
||||
},
|
||||
thumbsUp: {
|
||||
icon: 'icon-bbb-thumbs_up',
|
||||
intlId: 'app.actionsBar.emojiMenu.thumbsUpLabel',
|
||||
defaultMessage: 'Thumbs up',
|
||||
},
|
||||
thumbsDown: {
|
||||
icon: 'icon-bbb-thumbs_down',
|
||||
intlId: 'app.actionsBar.emojiMenu.thumbsDownLabel',
|
||||
defaultMessage: 'Thumbs down',
|
||||
},
|
||||
raiseHand: {
|
||||
icon: 'icon-bbb-hand',
|
||||
intlId: 'app.actionsBar.emojiMenu.raiseHandLabel',
|
||||
defaultMessage: 'Raise hand',
|
||||
},
|
||||
};
|
||||
|
||||
export function getUserEmojisSummary(user, skipNames = null, start = null, end = null) {
|
||||
const userEmojis = {};
|
||||
user.emojis.forEach((emoji) => {
|
||||
if (typeof emojiConfigs[emoji.name] === 'undefined') return;
|
||||
if (skipNames != null && skipNames.split(',').indexOf(emoji.name) > -1) return;
|
||||
if (start != null && emoji.sentOn < start) return;
|
||||
if (end != null && emoji.sentOn > end) return;
|
||||
if (typeof userEmojis[emoji.name] === 'undefined') {
|
||||
userEmojis[emoji.name] = 0;
|
||||
}
|
||||
userEmojis[emoji.name] += 1;
|
||||
});
|
||||
return userEmojis;
|
||||
}
|
||||
|
||||
export function filterUserEmojis(user, skipNames = null, start = null, end = null) {
|
||||
const userEmojis = [];
|
||||
user.emojis.forEach((emoji) => {
|
||||
if (typeof emojiConfigs[emoji.name] === 'undefined') return;
|
||||
if (skipNames != null && skipNames.split(',').indexOf(emoji.name) > -1) return;
|
||||
if (start != null && emoji.sentOn < start) return;
|
||||
if (end != null && emoji.sentOn > end) return;
|
||||
userEmojis.push(emoji);
|
||||
});
|
||||
return userEmojis;
|
||||
}
|
20
bbb-learning-dashboard/src/services/ReactionService.js
Normal file
20
bbb-learning-dashboard/src/services/ReactionService.js
Normal file
@ -0,0 +1,20 @@
|
||||
export function getUserReactionsSummary(user) {
|
||||
const userReactions = {};
|
||||
user.reactions.forEach((reaction) => {
|
||||
if (typeof userReactions[reaction.name] === 'undefined') {
|
||||
userReactions[reaction.name] = 0;
|
||||
}
|
||||
userReactions[reaction.name] += 1;
|
||||
});
|
||||
return userReactions;
|
||||
}
|
||||
|
||||
export function filterUserReactions(user, start = null, end = null) {
|
||||
const userReactions = [];
|
||||
user.reactions.forEach((reaction) => {
|
||||
if (start != null && reaction.sentOn < start) return;
|
||||
if (end != null && reaction.sentOn > end) return;
|
||||
userReactions.push(reaction);
|
||||
});
|
||||
return userReactions;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { emojiConfigs, filterUserEmojis } from './EmojiService';
|
||||
import { filterUserReactions } from './ReactionService';
|
||||
|
||||
export function getActivityScore(user, allUsers, totalOfPolls) {
|
||||
if (user.isModerator) return 0;
|
||||
@ -21,19 +21,19 @@ export function getActivityScore(user, allUsers, totalOfPolls) {
|
||||
}
|
||||
|
||||
// Calculate points of Raise hand
|
||||
const usersRaiseHand = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
|
||||
const usersRaiseHand = allUsersArr.map((currUser) => currUser.raiseHand.length);
|
||||
const maxRaiseHand = Math.max(...usersRaiseHand);
|
||||
const userRaiseHand = user.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
|
||||
const userRaiseHand = user.raiseHand.length;
|
||||
if (maxRaiseHand > 0) {
|
||||
userPoints += (userRaiseHand / maxRaiseHand) * 2;
|
||||
}
|
||||
|
||||
// Calculate points of Emojis
|
||||
const usersEmojis = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
|
||||
const maxEmojis = Math.max(...usersEmojis);
|
||||
const userEmojis = user.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
|
||||
if (maxEmojis > 0) {
|
||||
userPoints += (userEmojis / maxEmojis) * 2;
|
||||
// Calculate points of Reactions
|
||||
const usersReactions = allUsersArr.map((currUser) => currUser.reactions.length);
|
||||
const maxReactions = Math.max(...usersReactions);
|
||||
const userReactions = user.reactions.length;
|
||||
if (maxReactions > 0) {
|
||||
userPoints += (userReactions / maxReactions) * 2;
|
||||
}
|
||||
|
||||
// Calculate points of Polls
|
||||
@ -101,8 +101,8 @@ const tableHeaderFields = [
|
||||
defaultMessage: 'Messages',
|
||||
},
|
||||
{
|
||||
id: 'colEmojis',
|
||||
defaultMessage: 'Emojis',
|
||||
id: 'colReactions',
|
||||
defaultMessage: 'Reactions',
|
||||
},
|
||||
{
|
||||
id: 'pollVotes',
|
||||
@ -130,10 +130,6 @@ export function makeUserCSVData(users, polls, intl) {
|
||||
const userRecords = {};
|
||||
const userValues = Object.values(users || {});
|
||||
const pollValues = Object.values(polls || {});
|
||||
const skipEmojis = Object
|
||||
.keys(emojiConfigs)
|
||||
.filter((emoji) => emoji !== 'raiseHand')
|
||||
.join(',');
|
||||
|
||||
for (let i = 0; i < userValues.length; i += 1) {
|
||||
const user = userValues[i];
|
||||
@ -155,9 +151,9 @@ export function makeUserCSVData(users, polls, intl) {
|
||||
talk: user.talk.totalTime > 0 ? tsToHHmmss(user.talk.totalTime) : '-',
|
||||
webcam: webcam > 0 ? tsToHHmmss(webcam) : '-',
|
||||
messages: user.totalOfMessages,
|
||||
raiseHand: filterUserEmojis(user, 'raiseHand').length,
|
||||
raiseHand: user.raiseHand.length,
|
||||
answers: Object.keys(user.answers).length,
|
||||
emojis: filterUserEmojis(user, skipEmojis).length,
|
||||
reactions: filterUserReactions(user).length,
|
||||
registeredOn: intl.formatDate(joinTime, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
|
@ -1372,7 +1372,6 @@
|
||||
"app.learningDashboard.userDetails.anonymousAnswer": "Anonymous Poll",
|
||||
"app.learningDashboard.userDetails.talkTime": "Talk Time",
|
||||
"app.learningDashboard.userDetails.messages": "Messages",
|
||||
"app.learningDashboard.userDetails.emojis": "Emojis",
|
||||
"app.learningDashboard.userDetails.raiseHands": "Raise Hands",
|
||||
"app.learningDashboard.userDetails.pollVotes": "Poll Votes",
|
||||
"app.learningDashboard.userDetails.onlineIndicator": "{0} online time",
|
||||
@ -1381,7 +1380,7 @@
|
||||
"app.learningDashboard.usersTable.colTalk": "Talk time",
|
||||
"app.learningDashboard.usersTable.colWebcam": "Webcam Time",
|
||||
"app.learningDashboard.usersTable.colMessages": "Messages",
|
||||
"app.learningDashboard.usersTable.colEmojis": "Emojis",
|
||||
"app.learningDashboard.usersTable.colReactions": "Reactions",
|
||||
"app.learningDashboard.usersTable.colRaiseHands": "Raise Hands",
|
||||
"app.learningDashboard.usersTable.colActivityScore": "Activity Score",
|
||||
"app.learningDashboard.usersTable.colStatus": "Status",
|
||||
|
Loading…
Reference in New Issue
Block a user