Added email stats overview to member details page (#1795)

refs https://github.com/TryGhost/Ghost/issues/12461

- Added "Emails received", "Emails opened", and "Avg. open rate" to member details
- Adjusted visual display of avatar, name and email. Made email a mailto link
- Rearranged Name and Email fields to reduce height
- Changed height of the notes textarea

Co-authored-by: Kevin Ansfield <kevin@lookingsideways.co.uk>
This commit is contained in:
Sanne de Vries 2020-12-09 14:26:00 +01:00 committed by GitHub
parent ffe0f84700
commit ecfb77b980
5 changed files with 88 additions and 55 deletions

View File

@ -1,22 +1,84 @@
<div class="flex items-stretch mt2">
<div class="flex-auto br4 shadow-1 bg-grouped-table mt2 flex flex-column justify-between items-stretch gh-member-settings-primary mr6">
<div class="pa5 pb0 pt4">
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="name" @classNames="max-width">
<label for="member-name">Name</label>
<GhTextInput @id="member-name" @name="name" @value={{this.scratchMember.name}} @tabindex="1"
@focus-out={{action "setProperty" "name" this.scratchMember.name}} />
<GhErrorMessage @errors={{member.errors}} @property="name" />
</GhFormGroup>
<div>
<div class="flex items-center pa5 pb3 pt6">
{{#if (or this.member.name this.member.email)}}
<GhMemberAvatar
@member={{this.member}}
@sizeClass={{if this.member.name 'f-subheadline fw4 lh-zero tracked-1' 'f-headline fw4 lh-zero tracked-1'}}
@containerClass="w20 h20 mr4 gh-member-detail-avatar"
/>
{{else}}
<div class="flex items-center justify-center br-100 w18 h18 mr4 gh-new-member-avatar">
<span class="gh-member-avatar-label f-subheadline fw4 lh-zero tracked-1">N</span>
</div>
{{/if}}
<div>
<h3 class="f2 fw6 ma0 pa0">
{{or this.member.name this.member.email}}
</h3>
<p class="f7 pa0 ma0 midlightgrey-d1">
{{#if (and this.member.name this.member.email)}}
<span class="darkgrey fw5"><a href="mailto:{{this.member.email}}">{{this.member.email}}</a></span>
{{/if}}
</p>
{{#unless this.member.isNew}}
<p class="f7 pa0 ma0 midgrey-d1 {{if this.member.name "nudge-bottom--2"}}">
{{#if this.member.geolocation}}
{{#if (eq this.member.geolocation.country_code "US")}}
{{this.member.geolocation.region}}, US
{{else}}
{{this.member.geolocation.country}}
{{/if}}
{{else}}
Unknown location
{{/if}}
Created on {{moment-format @member.createdAtUTC "D MMM YYYY"}}
</p>
{{/unless}}
</div>
</div>
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="email" @classNames="max-width">
<label for="member-email">Email</label>
<GhTextInput @value={{this.scratchMember.email}} @id="member-email" @name="email" @tabindex="2"
@autocapitalize="off" @autocorrect="off" @autocomplete="off"
@focus-out={{action "setProperty" "email" this.scratchMember.email}} />
<GhErrorMessage @errors={{this.member.errors}} @property="email" />
</GhFormGroup>
<div class="flex mb5 pa5 pt3 pb5 bb b--whitegrey">
<div class="flex-auto flex flex-column justify-center items-start">
<div class="f7 midgrey-d1">Emails received</div>
<div class="f2 fw6">{{@member.emailCount}}</div>
</div>
<div class="flex-auto flex flex-column justify-center items-start">
<div class="f7 midgrey-d1">Emails opened</div>
<div class="f2 fw6">{{@member.emailOpenedCount}}</div>
</div>
<div class="flex-auto flex flex-column justify-center items-start">
<div class="f7 midgrey-d1">Avg. open rate</div>
<div class="f2 fw6">
{{#if (is-empty @member.emailOpenRate)}}
<span data-tooltip="Insufficient data available"></span>
{{else}}
{{@member.emailOpenRate}}%
{{/if}}
</div>
</div>
</div>
<div class="flex">
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="name" @classNames="max-width pl5">
<label for="member-name">Name</label>
<GhTextInput @id="member-name" @name="name" @value={{this.scratchMember.name}} @tabindex="1"
@focus-out={{action "setProperty" "name" this.scratchMember.name}} />
<GhErrorMessage @errors={{member.errors}} @property="name" />
</GhFormGroup>
<GhFormGroup @errors={{this.member.errors}} @hasValidated={{this.member.hasValidated}} @property="email" @classNames="max-width pl4 pr5">
<label for="member-email">Email</label>
<GhTextInput @value={{this.scratchMember.email}} @id="member-email" @name="email" @tabindex="2"
@autocapitalize="off" @autocorrect="off" @autocomplete="off"
@focus-out={{action "setProperty" "email" this.scratchMember.email}} />
<GhErrorMessage @errors={{this.member.errors}} @property="email" />
</GhFormGroup>
</div>
</div>
<div class="pa5 pt5 pb6 bt b--whitegrey">
<div class="pa5 pb6 bt b--whitegrey">
<GhFormGroup @classNames="gh-members-subscribed-checkbox mb0">
<div class="flex justify-between items-center">
<div>
@ -34,8 +96,9 @@
</GhFormGroup>
</div>
</div>
<div class="flex-auto br4 shadow-1 bg-grouped-table mt2 flex flex-column items-stretch gh-member-settings-secondary">
<div class="pa5 pt4">
<div class="pa5 pt6 pb6">
<GhFormGroup>
<label for="label-input">Labels</label>
<GhMemberLabelInput @member={{this.member}} @triggerId="label-input" />

View File

@ -15,6 +15,8 @@ export default Model.extend(ValidationEngine, {
labels: hasMany('label', {embedded: 'always', async: false}),
comped: attr('boolean', {defaultValue: false}),
geolocation: attr('json-string'),
emailCount: attr('number'),
emailOpenedCount: attr('number'),
emailOpenRate: attr('number'),
ghostPaths: service(),

View File

@ -396,7 +396,7 @@ textarea.gh-member-details-textarea {
max-width: 100%;
min-width: auto;
min-height: 50px;
height: 98px;
height: 156px;
}
.gh-member-info-icon {

View File

@ -89,6 +89,12 @@ input {
max-width: 100%;
}
.form-group.pa5 {
max-width: 100%;
padding-left: calc(var(--grid-size) * 5);
padding-right: calc(var(--grid-size) * 5);
}
.form-group.mb0 {
margin-bottom: 0;
}

View File

@ -26,44 +26,6 @@
</GhCanvasHeader>
<form class="mb10 member-basic-info-form">
<div class="flex items-center mb10 bt b--lightgrey-d1 pt8">
{{#if (or this.member.name this.member.email)}}
<GhMemberAvatar
@member={{this.member}}
@sizeClass={{if this.member.name 'f-subheadline fw4 lh-zero tracked-1' 'f-headline fw4 lh-zero tracked-1'}}
@containerClass="w20 h20 mr4 gh-member-detail-avatar"
/>
{{else}}
<div class="flex items-center justify-center br-100 w18 h18 mr4 gh-new-member-avatar">
<span class="gh-member-avatar-label f-subheadline fw4 lh-zero tracked-1">N</span>
</div>
{{/if}}
<div>
<h3 class="f2 fw6 ma0 pa0">
{{or this.member.name this.member.email}}
</h3>
<p class="f7 pa0 ma0 midlightgrey-d1">
{{#if (and this.member.name this.member.email)}}
<span class="darkgrey fw5">{{this.member.email}}</span>
{{/if}}
</p>
{{#unless this.member.isNew}}
<p class="f7 pa0 ma0 midgrey-d1 {{if this.member.name "nudge-bottom--2"}}">
{{#if this.member.geolocation}}
{{#if (eq this.member.geolocation.country_code "US")}}
{{this.member.geolocation.region}}, US
{{else}}
{{this.member.geolocation.country}}
{{/if}}
{{else}}
Unknown location
{{/if}}
Created on {{this.subscribedAt}}
</p>
{{/unless}}
</div>
</div>
<GhMemberSettingsForm
@member={{this.member}}
@scratchMember={{this.scratchMember}}