UX: Add tabs to filter between different types of RSVPed guests (#174)
* UX: Adding tabs to invitees modal Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This commit is contained in:
parent
edaeeb2402
commit
8bd20e6636
|
@ -13,6 +13,10 @@ module DiscoursePostEvent
|
|||
.where('LOWER(users.username) LIKE :filter', filter: "%#{params[:filter].downcase}%")
|
||||
end
|
||||
|
||||
if params[:type]
|
||||
event_invitees = event_invitees.with_status(params[:type].to_sym)
|
||||
end
|
||||
|
||||
render json: ActiveModel::ArraySerializer.new(
|
||||
event_invitees.order([:status, :user_id]).limit(200),
|
||||
each_serializer: InviteeSerializer
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import Component from "@ember/component";
|
||||
import { readOnly } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
tagName: "",
|
||||
viewingType: readOnly("modal.type"),
|
||||
|
||||
@discourseComputed("viewingType")
|
||||
isGoing(viewingType) {
|
||||
return viewingType === "going" ? " btn-danger" : " btn-default";
|
||||
},
|
||||
|
||||
@discourseComputed("viewingType")
|
||||
isInterested(viewingType) {
|
||||
return viewingType === "interested" ? " btn-danger" : " btn-default";
|
||||
},
|
||||
|
||||
@discourseComputed("viewingType")
|
||||
isNotGoing(viewingType) {
|
||||
return viewingType === "not_going" ? " btn-danger" : " btn-default";
|
||||
},
|
||||
});
|
|
@ -7,10 +7,20 @@ export default Controller.extend(ModalFunctionality, {
|
|||
invitees: null,
|
||||
filter: null,
|
||||
isLoading: false,
|
||||
type: "going",
|
||||
|
||||
onShow() {
|
||||
this._fetchInvitees();
|
||||
},
|
||||
@action
|
||||
toggleViewingFilter(filter) {
|
||||
this.onFilterChanged(filter);
|
||||
},
|
||||
@action
|
||||
toggleType(type) {
|
||||
this.set("type", type);
|
||||
this._fetchInvitees(this.filter);
|
||||
},
|
||||
|
||||
@action
|
||||
onFilterChanged(filter) {
|
||||
|
@ -21,7 +31,7 @@ export default Controller.extend(ModalFunctionality, {
|
|||
debounceFunc = require("discourse-common/lib/debounce").default;
|
||||
} catch (_) {}
|
||||
|
||||
debounceFunc(this, this._fetchInvitees, filter, 250);
|
||||
debounceFunc(this, this._fetchInvitees, filter, this.type, 250);
|
||||
},
|
||||
|
||||
@action
|
||||
|
@ -36,6 +46,7 @@ export default Controller.extend(ModalFunctionality, {
|
|||
.findAll("discourse-post-event-invitee", {
|
||||
filter,
|
||||
post_id: this.model.id,
|
||||
type: this.type,
|
||||
})
|
||||
.then((invitees) => this.set("invitees", invitees))
|
||||
.finally(() => this.set("isLoading", false));
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<div class="invitees-type-filter">
|
||||
{{d-button
|
||||
label="discourse_post_event.models.invitee.status.going"
|
||||
class=(concat "btn toggle-going" isGoing)
|
||||
action=toggle
|
||||
actionParam="going"
|
||||
}}
|
||||
|
||||
{{d-button
|
||||
label="discourse_post_event.models.invitee.status.interested"
|
||||
class=(concat "btn toggle-interested" isInterested)
|
||||
action=toggle
|
||||
actionParam="interested"
|
||||
}}
|
||||
|
||||
{{d-button
|
||||
label="discourse_post_event.models.invitee.status.not_going"
|
||||
class=(concat "btn toggle-not-going" isNotGoing)
|
||||
action=toggle
|
||||
actionParam="not_going"
|
||||
}}
|
||||
</div>
|
|
@ -5,8 +5,9 @@
|
|||
class="filter"
|
||||
placeholderKey="discourse_post_event.invitees_modal.filter_placeholder"
|
||||
}}
|
||||
|
||||
{{toggle-invitees-modal modal=this toggle=(action "toggleType")}}
|
||||
{{#conditional-loading-spinner condition=isLoading}}
|
||||
{{#if invitees}}
|
||||
<ul class="invitees">
|
||||
{{#each invitees as |invitee|}}
|
||||
<li class="invitee">
|
||||
|
@ -31,5 +32,10 @@
|
|||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p class="no-users">
|
||||
{{i18n "discourse_post_event.models.invitee.no_users"}}
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/conditional-loading-spinner}}
|
||||
{{/d-modal-body}}
|
||||
|
|
|
@ -1,10 +1,35 @@
|
|||
.discourse-post-event-invitees-modal {
|
||||
.modal-body {
|
||||
padding: 0;
|
||||
}
|
||||
.modal-inner-container {
|
||||
min-width: 350px;
|
||||
}
|
||||
.loading-container {
|
||||
height: 40vh;
|
||||
overflow-y: scroll;
|
||||
padding: 0 1em 9px 1em;
|
||||
.no-users {
|
||||
text-align: center;
|
||||
font-size: var(--font-up-1);
|
||||
}
|
||||
}
|
||||
.invitees-type-filter {
|
||||
margin-bottom: 9px;
|
||||
display: flex;
|
||||
.btn {
|
||||
width: calc(100% / 3);
|
||||
margin: 0;
|
||||
border-radius: 0px;
|
||||
padding: 0.75em 0em;
|
||||
}
|
||||
}
|
||||
.filter {
|
||||
width: 100%;
|
||||
width: calc(100% - 2em);
|
||||
margin: 1em;
|
||||
}
|
||||
.invitees {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
flex-direction: column;
|
||||
|
||||
|
|
|
@ -288,6 +288,7 @@ en:
|
|||
ends_in_duration: "Ends %{duration}"
|
||||
models:
|
||||
invitee:
|
||||
no_users: "There are no users of this type."
|
||||
status:
|
||||
unknown: "Not interested"
|
||||
going: "Going"
|
||||
|
|
|
@ -16,6 +16,63 @@ module DiscoursePostEvent
|
|||
let(:topic_1) { Fabricate(:topic, user: user) }
|
||||
let(:post_1) { Fabricate(:post, user: user, topic: topic_1) }
|
||||
|
||||
describe "#index" do
|
||||
|
||||
context 'when params are included' do
|
||||
let(:invitee1) { Fabricate(:user, username: "Francis", name: "Francis") }
|
||||
let(:invitee2) { Fabricate(:user, username: "Francisco", name: "Francisco") }
|
||||
let(:invitee3) { Fabricate(:user, username: "Frank", name: "Frank") }
|
||||
let(:invitee4) { Fabricate(:user, username: "Franchesca", name: "Franchesca") }
|
||||
let(:post_event_1) {
|
||||
pe = Fabricate(:event, post: post_1)
|
||||
pe.create_invitees([{
|
||||
user_id: invitee1.id,
|
||||
status: Invitee.statuses[:going]
|
||||
},
|
||||
{
|
||||
user_id: invitee2.id,
|
||||
status: Invitee.statuses[:interested]
|
||||
},
|
||||
{
|
||||
user_id: invitee3.id,
|
||||
status: Invitee.statuses[:not_going]
|
||||
},
|
||||
{
|
||||
user_id: invitee4.id,
|
||||
status: Invitee.statuses[:going]
|
||||
}])
|
||||
pe
|
||||
}
|
||||
|
||||
it 'returns the correct amount of users when filtering the invitees by name' do
|
||||
get "/discourse-post-event/events/#{post_event_1.id}/invitees.json", params: {
|
||||
filter: "Franc"
|
||||
}
|
||||
filteredInvitees = response.parsed_body["invitees"]
|
||||
expect(filteredInvitees.count).to eq(3)
|
||||
end
|
||||
|
||||
it 'returns the correct amount of users when filtering the invitees by type' do
|
||||
get "/discourse-post-event/events/#{post_event_1.id}/invitees.json", params: {
|
||||
type: "interested"
|
||||
}
|
||||
filteredInvitees = response.parsed_body["invitees"]
|
||||
expect(filteredInvitees.count).to eq(1)
|
||||
end
|
||||
|
||||
it 'returns the correct amount of users when filtering the invitees by name and type' do
|
||||
get "/discourse-post-event/events/#{post_event_1.id}/invitees.json", params: {
|
||||
filter: "Franc",
|
||||
type: "going"
|
||||
}
|
||||
filteredInvitees = response.parsed_body["invitees"]
|
||||
expect(filteredInvitees.count).to eq(2)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'when a post event exists' do
|
||||
context 'when an invitee exists' do
|
||||
let(:invitee1) { Fabricate(:user) }
|
||||
|
|
Loading…
Reference in New Issue