mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 15:12:58 +03:00
Added rough "edit mode" for members table behind dev flag
no issue - initial UI for bulk-selection, focusing for now on first stage which is limited to select-all and delete actions - adds "Edit" button to the members table that shows the "selection" toolbar in place of the table header and shows a checkbox next to each member
This commit is contained in:
parent
947b53a057
commit
34c28dcc0e
@ -1,5 +1,10 @@
|
||||
<li class="gh-list-row gh-members-list-item {{if @member.is_loading "loading"}}" ...attributes>
|
||||
{{#if @member.is_loading}}
|
||||
{{#if @isEditing}}
|
||||
<div class="gh-list-data gh-members-list-checkbox">
|
||||
<input type="checkbox" checked={{@isSelected}} disabled>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="gh-list-data gh-members-list-basic gh-list-loadingcell">
|
||||
<div class="gh-list-loading-title"></div>
|
||||
<div class="gh-list-loading-detail"></div>
|
||||
@ -8,6 +13,12 @@
|
||||
<div class="gh-list-data gh-members-list-subscribed-at gh-list-cellwidth-20"></div>
|
||||
<div class="gh-list-data gh-members-list-chevron gh-list-cellwidth-chevron"></div>
|
||||
{{else}}
|
||||
{{#if @isEditing}}
|
||||
<div class="gh-list-data gh-members-list-checkbox">
|
||||
<input type="checkbox" checked={{@isSelected}} disabled>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-basic">
|
||||
<div class="flex items-center">
|
||||
<GhMemberAvatar @member={{@member}} @containerClass="w9 h9 mr3 flex-shrink-0" />
|
||||
@ -21,7 +32,7 @@
|
||||
</LinkTo>
|
||||
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-geolocation gh-list-cellwidth-20 middarkgrey f8 {{if (not @member.name) "gh-members-geolocation-noname"}}">
|
||||
{{#if @member.geolocation}}
|
||||
{{#if (and @member.geolocation @member.geolocation.country)}}
|
||||
{{#if (eq @member.geolocation.country_code "US")}}
|
||||
{{@member.geolocation.region}}, US
|
||||
{{else}}
|
||||
@ -34,14 +45,17 @@
|
||||
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-members-list-subscribed-at gh-list-cellwidth-20 middarkgrey f8 {{if (not @member.name) "gh-members-subscribed-noname"}}">
|
||||
{{#if @member.createdAtUTC}}
|
||||
{{moment-format @member.createdAtUTC "MMM DD, YYYY"}} <span class="midlightgrey">({{moment-from-now @member.createdAtUTC}})</span>
|
||||
{{moment-format @member.createdAtUTC "MMM DD, YYYY"}}
|
||||
<span class="midlightgrey">({{moment-from-now @member.createdAtUTC}})</span>
|
||||
{{/if}}
|
||||
</LinkTo>
|
||||
|
||||
<LinkTo @route="member" @model={{@member}} title="Member details" class="gh-list-data gh-list-cellwidth-chevron gh-members-list-chevron">
|
||||
<div class="flex items-center justify-end w-100 h-100">
|
||||
<span class="nr2">{{svg-jar "arrow-right" class="w6 h6 fill-midgrey pa1"}}</span>
|
||||
</div>
|
||||
{{#unless @isEditing}}
|
||||
<div class="flex items-center justify-end w-100 h-100">
|
||||
<span class="nr2">{{svg-jar "arrow-right" class="w6 h6 fill-midgrey pa1"}}</span>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</li>
|
@ -22,6 +22,7 @@ const PAID_PARAMS = [{
|
||||
}];
|
||||
|
||||
export default class MembersController extends Controller {
|
||||
@service config;
|
||||
@service ellaSparse;
|
||||
@service feature;
|
||||
@service membersStats;
|
||||
@ -34,11 +35,13 @@ export default class MembersController extends Controller {
|
||||
];
|
||||
|
||||
@tracked members = A([]);
|
||||
@tracked allSelected = false;
|
||||
@tracked searchText = '';
|
||||
@tracked searchParam = '';
|
||||
@tracked paidParam = null;
|
||||
@tracked label = null;
|
||||
@tracked modalLabel = null;
|
||||
@tracked isEditing = false;
|
||||
@tracked showLabelModal = false;
|
||||
|
||||
@tracked _availableLabels = A([]);
|
||||
@ -111,6 +114,20 @@ export default class MembersController extends Controller {
|
||||
return this.paidParams.findBy('value', this.paidParam) || {value: '!unknown'};
|
||||
}
|
||||
|
||||
get selectedCount() {
|
||||
return this.allSelected ? this.members.length : 0;
|
||||
}
|
||||
|
||||
get selectAllLabel() {
|
||||
let {members} = this;
|
||||
|
||||
if (this.allSelected) {
|
||||
return `All items selected (${formatNumber(members.length)})`;
|
||||
} else {
|
||||
return `Select all (${formatNumber(members.length)})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Actions -----------------------------------------------------------------
|
||||
|
||||
@action
|
||||
@ -121,6 +138,16 @@ export default class MembersController extends Controller {
|
||||
this.membersStats.fetch();
|
||||
}
|
||||
|
||||
@action
|
||||
toggleEditMode() {
|
||||
this.isEditing = !this.isEditing;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSelectAll() {
|
||||
this.allSelected = !this.allSelected;
|
||||
}
|
||||
|
||||
@action
|
||||
search(e) {
|
||||
this.searchTask.perform(e.target.value);
|
||||
@ -182,6 +209,13 @@ export default class MembersController extends Controller {
|
||||
this.paidParam = paid.value;
|
||||
}
|
||||
|
||||
@action
|
||||
confirmDeleteMembers() {
|
||||
let {members} = this;
|
||||
let count = `${formatNumber(members.length)} ${pluralize(members.length, 'member', {withoutCount: true})}`;
|
||||
alert(`Once deletion is implemented, you'll see a confirmation for deleting ${count} here`);
|
||||
}
|
||||
|
||||
// Tasks -------------------------------------------------------------------
|
||||
|
||||
@task({restartable: true})
|
||||
|
@ -59,6 +59,32 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.members-list-header-overlay {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
position: sticky;
|
||||
height: 40px;
|
||||
top: calc(84px - 40px); /* match gh-list-header sticky top */
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: -40px;
|
||||
transform: translateY(40px);
|
||||
z-index: 2;
|
||||
|
||||
border-radius: 5px 5px 0 0;
|
||||
|
||||
border-bottom: 1px solid #e5eff5;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .1px;
|
||||
color: #738a94;
|
||||
text-transform: uppercase;
|
||||
padding: 10px 16px;
|
||||
white-space: nowrap;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.members-header .view-actions input.gh-members-list-searchfield {
|
||||
min-width: 220px;
|
||||
padding-left: 30px;
|
||||
@ -78,6 +104,10 @@
|
||||
box-shadow: inset 0 0 0 1px #3eb0ef;
|
||||
}
|
||||
|
||||
.gh-members-list-checkbox {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
p.gh-members-list-email {
|
||||
margin: -2px 0 -1px;
|
||||
}
|
||||
|
@ -57,16 +57,51 @@
|
||||
{{/if}}
|
||||
|
||||
<section class="content-list">
|
||||
{{!-- overlaid on header to keep table column sizing --}}
|
||||
{{#if this.isEditing}}
|
||||
<div class="members-list-header-overlay">
|
||||
<div class="flex flex-row">
|
||||
<div>
|
||||
<input type="checkbox" id="select-all-members" name="select-all-members" {{on "input" this.toggleSelectAll}}>
|
||||
<label for="select-all-members">{{this.selectAllLabel}}</label>
|
||||
</div>
|
||||
<button class="gh-btn" {{on "click" this.confirmDeleteMembers}} disabled={{not this.selectedCount}}>
|
||||
<span>Delete</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button class="gh-btn" {{on "click" this.toggleEditMode}}>
|
||||
<span>Done</span>
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<ol class="members-list gh-list {{unless this.members "no-posts"}}">
|
||||
{{#if this.members}}
|
||||
<li class="gh-list-row header">
|
||||
<li class="gh-list-row header relative">
|
||||
{{#if this.isEditing}}
|
||||
{{!-- necessary because we add an extra column in the list items --}}
|
||||
<div class="gh-list-header gh-members-list-checkbox"></div>
|
||||
{{/if}}
|
||||
<div class="gh-list-header">{{this.listHeader}}</div>
|
||||
<div class="gh-list-header gh-members-list-geolocation gh-list-cellwidth-20 nowrap">Location</div>
|
||||
<div class="gh-list-header gh-members-list-subscribed-at gh-list-cellwidth-20 nowrap">Created</div>
|
||||
<div class="gh-list-header gh-members-list-chevron gh-list-cellwidth-chevron"></div>
|
||||
<div class="gh-list-header gh-members-list-chevron gh-list-cellwidth-chevron">
|
||||
{{!-- TODO: 🍆🖌 --}}
|
||||
{{#if this.config.enableDeveloperExperiments}}
|
||||
<button class="gh-btn" style="position: absolute; top: 2px; right: 3px" {{on "click" this.toggleEditMode}}>
|
||||
<span>Edit</span>
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
<VerticalCollection @items={{this.members}} @key="id" @containerSelector=".gh-main" @estimateHeight={{69}} @staticHeight={{true}} @bufferSize={{20}} as |member|>
|
||||
<GhMembersListItem @member={{member}} @data-test-member-id={{member.id}} />
|
||||
<GhMembersListItem
|
||||
@member={{member}}
|
||||
@isEditing={{this.isEditing}}
|
||||
@isSelected={{this.allSelected}}
|
||||
@data-test-member-id={{member.id}}
|
||||
/>
|
||||
</VerticalCollection>
|
||||
{{else}}
|
||||
<li class="no-posts-box">
|
||||
|
Loading…
Reference in New Issue
Block a user