diff --git a/app/controllers/discourse_subscriptions/admin/coupons_controller.rb b/app/controllers/discourse_subscriptions/admin/coupons_controller.rb new file mode 100644 index 0000000..072a5be --- /dev/null +++ b/app/controllers/discourse_subscriptions/admin/coupons_controller.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module DiscourseSubscriptions + module Admin + class CouponsController < ::Admin::AdminController + include DiscourseSubscriptions::Stripe + include DiscourseSubscriptions::Group + before_action :set_api_key + + def index + begin + promo_codes = ::Stripe::PromotionCode.list({ limit: 100 })[:data] + promo_codes = promo_codes.select { |code| code[:coupon][:valid] == true } + render_json_dump promo_codes + rescue ::Stripe::InvalidRequestError => e + render_json_error e.message + end + end + + def create + params.require([:promo, :discount_type, :discount, :active]) + begin + coupon_params = { + duration: 'forever', + } + + case params[:discount_type] + when 'amount' + coupon_params[:amount_off] = params[:discount].to_i * 100 + coupon_params[:currency] = SiteSetting.discourse_subscriptions_currency + when 'percent' + coupon_params[:percent_off] = params[:discount] + end + + coupon = ::Stripe::Coupon.create(coupon_params) + + promo_code = ::Stripe::PromotionCode.create({ coupon: coupon[:id], code: params[:promo] }) if coupon.present? + + render_json_dump promo_code + rescue ::Stripe::InvalidRequestError => e + render_json_error e.message + end + end + + def update + params.require([:id, :active]) + begin + promo_code = ::Stripe::PromotionCode.update( + params[:id], + { + active: params[:active] + } + ) + + render_json_dump promo_code + rescue ::Stripe::InvalidRequestError => e + render_json_error e.message + end + end + + def destroy + params.require(:coupon_id) + begin + coupon = ::Stripe::Coupon.delete(params[:coupon_id]) + render_json_dump coupon + rescue ::Stripe::InvalidRequestError => e + render_json_error e.message + end + end + end + end +end diff --git a/assets/javascripts/discourse/components/create-coupon-form.js.es6 b/assets/javascripts/discourse/components/create-coupon-form.js.es6 new file mode 100644 index 0000000..338a4e8 --- /dev/null +++ b/assets/javascripts/discourse/components/create-coupon-form.js.es6 @@ -0,0 +1,32 @@ +import Component from "@ember/component"; +import discourseComputed from "discourse-common/utils/decorators"; + +export default Component.extend({ + @discourseComputed + discountTypes() { + return [ + { id: "amount", name: "Amount" }, + { id: "percent", name: "Percent" }, + ]; + }, + discountType: "amount", + discount: null, + promoCode: null, + active: false, + + actions: { + createNewCoupon() { + const createParams = { + promo: this.promoCode, + discount_type: this.discountType, + discount: this.discount, + active: this.active, + }; + + this.create(createParams); + }, + cancelCreate() { + this.cancel(); + }, + }, +}); diff --git a/assets/javascripts/discourse/controllers/admin-plugins-discourse-subscriptions-coupons.js.es6 b/assets/javascripts/discourse/controllers/admin-plugins-discourse-subscriptions-coupons.js.es6 new file mode 100644 index 0000000..14ea1d7 --- /dev/null +++ b/assets/javascripts/discourse/controllers/admin-plugins-discourse-subscriptions-coupons.js.es6 @@ -0,0 +1,42 @@ +import Controller from "@ember/controller"; +import AdminCoupon from "discourse/plugins/discourse-subscriptions/discourse/models/admin-coupon"; +import { popupAjaxError } from "discourse/lib/ajax-error"; + +export default Controller.extend({ + creating: null, + + actions: { + openCreateForm() { + this.set("creating", true); + }, + closeCreateForm() { + this.set("creating", false); + }, + createNewCoupon(params) { + AdminCoupon.save(params) + .then(() => { + this.send("closeCreateForm"); + this.send("reloadModel"); + }) + .catch(popupAjaxError); + }, + deleteCoupon(coupon) { + AdminCoupon.destroy(coupon) + .then(() => { + this.send("reloadModel"); + }) + .catch(popupAjaxError); + }, + toggleActive(coupon) { + const couponData = { + id: coupon.id, + active: !coupon.active, + }; + AdminCoupon.update(couponData) + .then(() => { + this.send("reloadModel"); + }) + .catch(popupAjaxError); + }, + }, +}); diff --git a/assets/javascripts/discourse/discourse-subscriptions-route-map.js.es6 b/assets/javascripts/discourse/discourse-subscriptions-route-map.js.es6 index 0ea72b8..be24a39 100644 --- a/assets/javascripts/discourse/discourse-subscriptions-route-map.js.es6 +++ b/assets/javascripts/discourse/discourse-subscriptions-route-map.js.es6 @@ -14,6 +14,8 @@ export default { }); }); + this.route("coupons"); + this.route("subscriptions"); }); }, diff --git a/assets/javascripts/discourse/models/admin-coupon.js.es6 b/assets/javascripts/discourse/models/admin-coupon.js.es6 new file mode 100644 index 0000000..e3b12d5 --- /dev/null +++ b/assets/javascripts/discourse/models/admin-coupon.js.es6 @@ -0,0 +1,64 @@ +import { ajax } from "discourse/lib/ajax"; +import EmberObject from "@ember/object"; +import discourseComputed from "discourse-common/utils/decorators"; + +const AdminCoupon = EmberObject.extend({ + @discourseComputed("coupon.amount_off", "coupon.percent_off") + discount(amount_off, percent_off) { + if (amount_off) { + return `${parseFloat(amount_off * 0.01).toFixed(2)}`; + } else if (percent_off) { + return `${percent_off}%`; + } + }, +}); + +AdminCoupon.reopenClass({ + list() { + return ajax("/s/admin/coupons", { + method: "get", + }).then((result) => { + if (result === null) { + return { unconfigured: true }; + } + return result.map((coupon) => AdminCoupon.create(coupon)); + }); + }, + save(params) { + const data = { + promo: params.promo, + discount_type: params.discount_type, + discount: params.discount, + active: params.active, + }; + + return ajax("/s/admin/coupons", { + method: "post", + data, + }).then((coupon) => AdminCoupon.create(coupon)); + }, + + update(params) { + const data = { + id: params.id, + active: params.active, + }; + + return ajax("/s/admin/coupons", { + method: "put", + data, + }).then((coupon) => AdminCoupon.create(coupon)); + }, + + destroy(params) { + const data = { + coupon_id: params.coupon.id, + }; + return ajax("/s/admin/coupons", { + method: "delete", + data, + }); + }, +}); + +export default AdminCoupon; diff --git a/assets/javascripts/discourse/routes/admin-plugins-discourse-subscriptions-coupons.js.es6 b/assets/javascripts/discourse/routes/admin-plugins-discourse-subscriptions-coupons.js.es6 new file mode 100644 index 0000000..85db462 --- /dev/null +++ b/assets/javascripts/discourse/routes/admin-plugins-discourse-subscriptions-coupons.js.es6 @@ -0,0 +1,14 @@ +import Route from "@ember/routing/route"; +import AdminCoupon from "discourse/plugins/discourse-subscriptions/discourse/models/admin-coupon"; + +export default Route.extend({ + model() { + return AdminCoupon.list(); + }, + + actions: { + reloadModel() { + this.refresh(); + }, + }, +}); diff --git a/assets/javascripts/discourse/templates/admin/plugins-discourse-subscriptions-coupons.hbs b/assets/javascripts/discourse/templates/admin/plugins-discourse-subscriptions-coupons.hbs new file mode 100644 index 0000000..6083eb6 --- /dev/null +++ b/assets/javascripts/discourse/templates/admin/plugins-discourse-subscriptions-coupons.hbs @@ -0,0 +1,44 @@ +{{#if model.unconfigured}} +
{{i18n 'discourse_subscriptions.admin.unconfigured'}}
+Discourse Subscriptions on Meta
+{{else}} + {{#if model}} +{{i18n 'discourse_subscriptions.admin.coupons.code'}} | +{{i18n 'discourse_subscriptions.admin.coupons.discount'}} | +{{i18n 'discourse_subscriptions.admin.coupons.times_redeemed'}} | +{{i18n 'discourse_subscriptions.admin.coupons.active'}} | +{{i18n 'discourse_subscriptions.admin.coupons.actions'}} | + + {{#each model as |coupon|}} +
---|---|---|---|---|
{{coupon.code}} | +{{coupon.discount}} | +{{coupon.times_redeemed}} | +{{input type="checkbox" checked=coupon.active click=(action "toggleActive" coupon)}} | ++ {{d-button + action=(action "deleteCoupon") + actionParam=coupon + icon="trash-alt" + class="btn-danger btn btn-icon btn-no-text"}} + | +