mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-24 23:48:13 -05:00
Humanized event details on members activity screen
refs https://github.com/TryGhost/Team/issues/1277 - moved event parsing from a component to a helper - keeps code as a plain function - allows for per-event parsing which helps with rendering as we're not rebuilding/re-rendering a whole array each time the events data changes when a new page is loaded - updated to include full member and email objects (will be used later for email preview popup) - updated members-activity table row component to use the event parser helper for better event details
This commit is contained in:
parent
5da8ee3c76
commit
acbc4233a8
5 changed files with 143 additions and 139 deletions
|
@ -17,31 +17,34 @@
|
|||
{{/if}}
|
||||
|
||||
{{#unless (or eventsFetcher.isLoading eventsFetcher.isError)}}
|
||||
<GhMemberActivityEventParser @events={{eventsFetcher.data}} as |parsedEvents|>
|
||||
<div class="gh-event-timeline">
|
||||
{{#if parsedEvents}}
|
||||
<ul class="gh-dashboard-activity-feed">
|
||||
{{#each parsedEvents as |event|}}
|
||||
<div class="gh-event-timeline">
|
||||
{{#if eventsFetcher.data}}
|
||||
<ul class="gh-dashboard-activity-feed">
|
||||
{{#each eventsFetcher.data as |event|}}
|
||||
{{#let (parse-member-event event) as |parsedEvent|}}
|
||||
<li data-test-dashboard-member-activity-item>
|
||||
<LinkTo class="member-details" @route="member" @model="{{event.member_id}}">
|
||||
<LinkTo class="member-details" @route="member" @model="{{parsedEvent.memberId}}">
|
||||
<div class="activity">
|
||||
<div>
|
||||
<span class="member">{{event.subject}}</span> {{event.action}} {{event.object}} <span class="highlight">{{event.info}}</span>
|
||||
<span class="member">{{parsedEvent.subject}}</span>
|
||||
{{parsedEvent.action}}
|
||||
{{parsedEvent.object}}
|
||||
<span class="highlight">{{parsedEvent.info}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</LinkTo>
|
||||
<span class="time">{{event.timestamp}}</span>
|
||||
<span class="time">{{moment-from-now parsedEvent.timestamp}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<div class="gh-no-data-list" data-test-no-member-activities>
|
||||
{{svg-jar "no-data-list"}}
|
||||
<span>No member activity available.</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</GhMemberActivityEventParser>
|
||||
{{/let}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<div class="gh-no-data-list" data-test-no-member-activities>
|
||||
{{svg-jar "no-data-list"}}
|
||||
<span>No member activity available.</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if (feature "membersActivityFeed")}}
|
||||
<div class="gh-dashboard-top-members-footer">
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{{yield this.parsedEvents}}
|
|
@ -1,109 +0,0 @@
|
|||
import Component from '@glimmer/component';
|
||||
import moment from 'moment';
|
||||
import {getNonDecimal, getSymbol} from 'ghost-admin/utils/currency';
|
||||
|
||||
export default class EventTimeline extends Component {
|
||||
get parsedEvents() {
|
||||
if (!this.args.events) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.args.events.map((event) => {
|
||||
let subject = event.data.member.name || event.data.member.email;
|
||||
let icon = this.getIcon(event);
|
||||
let action = this.getAction(event);
|
||||
let object = this.getObject(event);
|
||||
let info = this.getInfo(event);
|
||||
let timestamp = moment(event.data.created_at).fromNow();
|
||||
|
||||
return {
|
||||
member_id: event.data.member_id,
|
||||
icon,
|
||||
subject,
|
||||
action,
|
||||
object,
|
||||
info,
|
||||
timestamp
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getIcon(event) {
|
||||
return event.type;
|
||||
}
|
||||
|
||||
getAction(event) {
|
||||
if (event.type === 'signup_event') {
|
||||
return 'signed up';
|
||||
}
|
||||
|
||||
if (event.type === 'login_event') {
|
||||
return 'logged in';
|
||||
}
|
||||
|
||||
if (event.type === 'payment_event') {
|
||||
return 'made a payment';
|
||||
}
|
||||
|
||||
if (event.type === 'newsletter_event') {
|
||||
if (event.data.subscribed) {
|
||||
return 'subscribed to';
|
||||
} else {
|
||||
return 'unsubscribed from';
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === 'subscription_event') {
|
||||
if (event.data.from_plan === null) {
|
||||
return 'started';
|
||||
}
|
||||
|
||||
if (event.data.to_plan === null) {
|
||||
return 'cancelled';
|
||||
}
|
||||
|
||||
return 'changed';
|
||||
}
|
||||
|
||||
if (event.type === 'email_opened_event') {
|
||||
return 'opened';
|
||||
}
|
||||
|
||||
if (event.type === 'email_delivered_event') {
|
||||
return 'received';
|
||||
}
|
||||
|
||||
if (event.type === 'email_failed_event') {
|
||||
return 'failed to receive';
|
||||
}
|
||||
}
|
||||
|
||||
getObject(event) {
|
||||
if (event.type === 'newsletter_event') {
|
||||
return 'emails';
|
||||
}
|
||||
|
||||
if (event.type === 'subscription_event') {
|
||||
return 'their subscription';
|
||||
}
|
||||
|
||||
if (event.type.match?.(/^email_/)) {
|
||||
return 'an email';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
getInfo(event) {
|
||||
if (event.type === 'subscription_event') {
|
||||
let mrrDelta = getNonDecimal(event.data.mrr_delta, event.data.currency);
|
||||
if (mrrDelta === 0) {
|
||||
return;
|
||||
}
|
||||
let sign = mrrDelta > 0 ? '+' : '-';
|
||||
let symbol = getSymbol(event.data.currency);
|
||||
return `(MRR ${sign}${symbol}${Math.abs(mrrDelta)})`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -1,11 +1,17 @@
|
|||
<tr>
|
||||
<div class="gh-list-data">
|
||||
<LinkTo @route="member" @model={{@event.data.member.id}}>
|
||||
<strong>{{@event.data.member.name}}</strong>
|
||||
<br>
|
||||
{{@event.data.member.email}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
<div class="gh-list-data">{{@event.type}}</div>
|
||||
<div class="gh-list-data">{{moment-format @event.data.created_at "D MMM YYYY HH:MM"}}</div>
|
||||
</tr>
|
||||
{{#let (parse-member-event @event) as |event|}}
|
||||
<tr>
|
||||
<div class="gh-list-data">
|
||||
<LinkTo @route="member" @model={{event.memberId}}>
|
||||
<strong>{{event.member.name}}</strong>
|
||||
<br>
|
||||
{{event.member.email}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
<div class="gh-list-data">
|
||||
{{event.action}}
|
||||
{{event.object}}
|
||||
<span class="highlight">{{event.info}}</span>
|
||||
</div>
|
||||
<div class="gh-list-data">{{moment-format event.timestamp "D MMM YYYY HH:MM"}}</div>
|
||||
</tr>
|
||||
{{/let}}
|
105
ghost/admin/app/helpers/parse-member-event.js
Normal file
105
ghost/admin/app/helpers/parse-member-event.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
import moment from 'moment';
|
||||
import {getNonDecimal, getSymbol} from 'ghost-admin/utils/currency';
|
||||
|
||||
export default function parseMemberEvent(event) {
|
||||
let subject = event.data.member.name || event.data.member.email;
|
||||
let icon = getIcon(event);
|
||||
let action = getAction(event);
|
||||
let object = getObject(event);
|
||||
let info = getInfo(event);
|
||||
let timestamp = moment(event.data.created_at);
|
||||
|
||||
return {
|
||||
memberId: event.data.member_id,
|
||||
member: event.data.member,
|
||||
emailId: event.data.email_id,
|
||||
email: event.data.email,
|
||||
icon,
|
||||
subject,
|
||||
action,
|
||||
object,
|
||||
info,
|
||||
timestamp
|
||||
};
|
||||
}
|
||||
|
||||
/* internal helper functions */
|
||||
|
||||
function getIcon(event) {
|
||||
return event.type;
|
||||
}
|
||||
|
||||
function getAction(event) {
|
||||
if (event.type === 'signup_event') {
|
||||
return 'signed up';
|
||||
}
|
||||
|
||||
if (event.type === 'login_event') {
|
||||
return 'logged in';
|
||||
}
|
||||
|
||||
if (event.type === 'payment_event') {
|
||||
return 'made a payment';
|
||||
}
|
||||
|
||||
if (event.type === 'newsletter_event') {
|
||||
if (event.data.subscribed) {
|
||||
return 'subscribed to';
|
||||
} else {
|
||||
return 'unsubscribed from';
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === 'subscription_event') {
|
||||
if (event.data.from_plan === null) {
|
||||
return 'started';
|
||||
}
|
||||
|
||||
if (event.data.to_plan === null) {
|
||||
return 'cancelled';
|
||||
}
|
||||
|
||||
return 'changed';
|
||||
}
|
||||
|
||||
if (event.type === 'email_opened_event') {
|
||||
return 'opened';
|
||||
}
|
||||
|
||||
if (event.type === 'email_delivered_event') {
|
||||
return 'received';
|
||||
}
|
||||
|
||||
if (event.type === 'email_failed_event') {
|
||||
return 'failed to receive';
|
||||
}
|
||||
}
|
||||
|
||||
function getObject(event) {
|
||||
if (event.type === 'newsletter_event') {
|
||||
return 'emails';
|
||||
}
|
||||
|
||||
if (event.type === 'subscription_event') {
|
||||
return 'their subscription';
|
||||
}
|
||||
|
||||
if (event.type.match?.(/^email_/)) {
|
||||
return 'an email';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function getInfo(event) {
|
||||
if (event.type === 'subscription_event') {
|
||||
let mrrDelta = getNonDecimal(event.data.mrr_delta, event.data.currency);
|
||||
if (mrrDelta === 0) {
|
||||
return;
|
||||
}
|
||||
let sign = mrrDelta > 0 ? '+' : '-';
|
||||
let symbol = getSymbol(event.data.currency);
|
||||
return `(MRR ${sign}${symbol}${Math.abs(mrrDelta)})`;
|
||||
}
|
||||
return;
|
||||
}
|
Loading…
Add table
Reference in a new issue