FEATURE: Admin > Subscriptions Pagination (#50)
Meta topic: https://meta.discourse.org/t/subscriptions-add-pagination-to-admin-subscriptions-view/172500 This adds support for pagination using our `{{load-more}}` component in core. Implementation on the backend was a bit tricky because we don't return all results from Stripe, only those that match local subscriptions stored in the `DiscourseSubscriptions::Subscription` model.
This commit is contained in:
parent
eaf1729f6f
commit
a282475da3
|
@ -7,14 +7,33 @@ module DiscourseSubscriptions
|
|||
include DiscourseSubscriptions::Group
|
||||
before_action :set_api_key
|
||||
|
||||
PAGE_LIMIT = 10
|
||||
|
||||
def index
|
||||
begin
|
||||
subscription_ids = Subscription.all.pluck(:external_id)
|
||||
subscriptions = []
|
||||
subscriptions = {
|
||||
has_more: false,
|
||||
data: [],
|
||||
length: 0,
|
||||
last_record: params[:last_record]
|
||||
}
|
||||
|
||||
if subscription_ids.present? && is_stripe_configured?
|
||||
subscriptions = ::Stripe::Subscription.list(expand: ['data.plan.product'])
|
||||
subscriptions = subscriptions.select { |sub| subscription_ids.include?(sub[:id]) }
|
||||
while subscriptions[:length] < PAGE_LIMIT
|
||||
current_set = get_subscriptions(subscriptions[:last_record])
|
||||
|
||||
until valid_subscriptions = find_valid_subscriptions(current_set[:data], subscription_ids) do
|
||||
current_set = get_subscriptions(current_set[:data].last)
|
||||
break if current_set[:has_more] == false
|
||||
end
|
||||
|
||||
subscriptions[:data] = subscriptions[:data].concat(valid_subscriptions.to_a)
|
||||
subscriptions[:last_record] = current_set[:data].last[:id] if current_set[:data].present?
|
||||
subscriptions[:length] = subscriptions[:data].length
|
||||
subscriptions[:has_more] = current_set[:has_more]
|
||||
break if subscriptions[:has_more] == false
|
||||
end
|
||||
elsif !is_stripe_configured?
|
||||
subscriptions = nil
|
||||
end
|
||||
|
@ -54,6 +73,15 @@ module DiscourseSubscriptions
|
|||
|
||||
private
|
||||
|
||||
def get_subscriptions(start)
|
||||
::Stripe::Subscription.list(expand: ['data.plan.product'], limit: PAGE_LIMIT, starting_after: start)
|
||||
end
|
||||
|
||||
def find_valid_subscriptions(data, ids)
|
||||
valid = data.select { |sub| ids.include?(sub[:id]) }
|
||||
valid.empty? ? nil : valid
|
||||
end
|
||||
|
||||
# this will only refund the most recent subscription payment
|
||||
def refund_subscription(subscription_id)
|
||||
subscription = ::Stripe::Subscription.retrieve(subscription_id)
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
import AdminSubscription from "discourse/plugins/discourse-subscriptions/discourse/models/admin-subscription";
|
||||
import Controller from "@ember/controller";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
export default Controller.extend({
|
||||
loading: false,
|
||||
|
||||
actions: {
|
||||
showCancelModal(subscription) {
|
||||
showModal("admin-cancel-subscription", {
|
||||
model: subscription,
|
||||
});
|
||||
},
|
||||
|
||||
loadMore() {
|
||||
if (!this.loading && this.model.has_more) {
|
||||
this.set("loading", true);
|
||||
|
||||
return AdminSubscription.loadMore(this.model.last_record).then(
|
||||
(result) => {
|
||||
const updated = this.model.data.concat(result.data);
|
||||
this.set("model", result);
|
||||
this.set("model.data", updated);
|
||||
this.set("loading", false);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -38,9 +38,20 @@ AdminSubscription.reopenClass({
|
|||
if (result === null) {
|
||||
return { unconfigured: true };
|
||||
}
|
||||
return result.map((subscription) =>
|
||||
result.data = result.data.map((subscription) =>
|
||||
AdminSubscription.create(subscription)
|
||||
);
|
||||
return result;
|
||||
});
|
||||
},
|
||||
loadMore(lastRecord) {
|
||||
return ajax(`/s/admin/subscriptions?last_record=${lastRecord}`, {
|
||||
method: "get",
|
||||
}).then((result) => {
|
||||
result.data = result.data.map((subscription) =>
|
||||
AdminSubscription.create(subscription)
|
||||
);
|
||||
return result;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -2,42 +2,45 @@
|
|||
<p>{{i18n 'discourse_subscriptions.admin.unconfigured'}}</p>
|
||||
<p><a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">Discourse Subscriptions on Meta</a></p>
|
||||
{{else}}
|
||||
<table class="table discourse-patrons-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.user'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.subscription_id'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.customer'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.product'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.plan'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.status'}}</th>
|
||||
<th class="td-right">{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.created_at'}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#each model as |subscription|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#if subscription.metadataUserExists}}
|
||||
<a href="{{unbound subscription.subscriptionUserPath}}">
|
||||
{{subscription.metadata.username}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>{{subscription.id}}</td>
|
||||
<td>{{subscription.customer}}</td>
|
||||
<td>{{subscription.plan.product.name}}</td>
|
||||
<td>{{subscription.plan.nickname}}</td>
|
||||
<td>{{subscription.status}}</td>
|
||||
<td class="td-right">{{format-unix-date subscription.created}}</td>
|
||||
<td class="td-right">
|
||||
{{#if subscription.loading}}
|
||||
{{loading-spinner size="small"}}
|
||||
{{else}}
|
||||
{{d-button disabled=subscription.canceled label="cancel" action=(action "showCancelModal" subscription) icon="times"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{#load-more selector=".discourse-patrons-table tr" action=(action "loadMore")}}
|
||||
<table class="table discourse-patrons-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.user'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.subscription_id'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.customer'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.product'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.plan'}}</th>
|
||||
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.status'}}</th>
|
||||
<th class="td-right">{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.created_at'}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{#each model.data as |subscription|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#if subscription.metadataUserExists}}
|
||||
<a href="{{unbound subscription.subscriptionUserPath}}">
|
||||
{{subscription.metadata.username}}
|
||||
</a>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td>{{subscription.id}}</td>
|
||||
<td>{{subscription.customer}}</td>
|
||||
<td>{{subscription.plan.product.name}}</td>
|
||||
<td>{{subscription.plan.nickname}}</td>
|
||||
<td>{{subscription.status}}</td>
|
||||
<td class="td-right">{{format-unix-date subscription.created}}</td>
|
||||
<td class="td-right">
|
||||
{{#if subscription.loading}}
|
||||
{{loading-spinner size="small"}}
|
||||
{{else}}
|
||||
{{d-button disabled=subscription.canceled label="cancel" action=(action "showCancelModal" subscription) icon="times"}}
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/load-more}}
|
||||
{{conditional-loading-spinner condition=loading}}
|
||||
{{/if}}
|
||||
|
|
|
@ -13,6 +13,7 @@ module DiscourseSubscriptions
|
|||
|
||||
before do
|
||||
Fabricate(:subscription, external_id: "sub_12345", customer_id: customer.id)
|
||||
Fabricate(:subscription, external_id: "sub_77777", customer_id: customer.id)
|
||||
end
|
||||
|
||||
context 'unauthenticated' do
|
||||
|
@ -34,21 +35,44 @@ module DiscourseSubscriptions
|
|||
before { sign_in(admin) }
|
||||
|
||||
describe "index" do
|
||||
it "gets the subscriptions and products" do
|
||||
before do
|
||||
SiteSetting.discourse_subscriptions_public_key = "public-key"
|
||||
SiteSetting.discourse_subscriptions_secret_key = "secret-key"
|
||||
::Stripe::Subscription.expects(:list).with(expand: ['data.plan.product']).returns(
|
||||
[
|
||||
{ id: "sub_12345" },
|
||||
{ id: "sub_nope" }
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
it "gets the subscriptions and products" do
|
||||
::Stripe::Subscription.expects(:list)
|
||||
.with(expand: ['data.plan.product'], limit: 10, starting_after: nil)
|
||||
.returns(
|
||||
has_more: false,
|
||||
data: [
|
||||
{ id: "sub_12345" },
|
||||
{ id: "sub_nope" }
|
||||
]
|
||||
)
|
||||
get "/s/admin/subscriptions.json"
|
||||
subscriptions = response.parsed_body[0]["id"]
|
||||
subscriptions = response.parsed_body["data"][0]["id"]
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(subscriptions).to eq("sub_12345")
|
||||
end
|
||||
|
||||
it "handles starting at a different point in the set" do
|
||||
::Stripe::Subscription.expects(:list)
|
||||
.with(expand: ['data.plan.product'], limit: 10, starting_after: 'sub_nope')
|
||||
.returns(
|
||||
has_more: false,
|
||||
data: [
|
||||
{ id: "sub_77777" },
|
||||
{ id: "sub_yepnoep" }
|
||||
]
|
||||
)
|
||||
get "/s/admin/subscriptions.json", params: { last_record: 'sub_nope' }
|
||||
subscriptions = response.parsed_body["data"][0]["id"]
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(subscriptions).to eq("sub_77777")
|
||||
end
|
||||
end
|
||||
|
||||
describe "destroy" do
|
||||
|
|
Loading…
Reference in New Issue