Merge e0e0596670
into 33d033c23b
This commit is contained in:
commit
5060910450
38
Gemfile.lock
38
Gemfile.lock
|
@ -20,30 +20,31 @@ GEM
|
||||||
securerandom (>= 0.3)
|
securerandom (>= 0.3)
|
||||||
tzinfo (~> 2.0, >= 2.0.5)
|
tzinfo (~> 2.0, >= 2.0.5)
|
||||||
uri (>= 0.13.1)
|
uri (>= 0.13.1)
|
||||||
ast (2.4.2)
|
ast (2.4.3)
|
||||||
base64 (0.2.0)
|
base64 (0.2.0)
|
||||||
benchmark (0.4.0)
|
benchmark (0.4.0)
|
||||||
bigdecimal (3.1.9)
|
bigdecimal (3.1.9)
|
||||||
concurrent-ruby (1.3.5)
|
concurrent-ruby (1.3.5)
|
||||||
connection_pool (2.5.0)
|
connection_pool (2.5.3)
|
||||||
drb (2.2.1)
|
drb (2.2.3)
|
||||||
i18n (1.14.7)
|
i18n (1.14.7)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
json (2.10.2)
|
json (2.12.2)
|
||||||
language_server-protocol (3.17.0.4)
|
language_server-protocol (3.17.0.5)
|
||||||
lint_roller (1.1.0)
|
lint_roller (1.1.0)
|
||||||
logger (1.6.6)
|
logger (1.7.0)
|
||||||
minitest (5.25.5)
|
minitest (5.25.5)
|
||||||
parallel (1.26.3)
|
parallel (1.27.0)
|
||||||
parser (3.3.7.1)
|
parser (3.3.8.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
prettier_print (1.2.1)
|
prettier_print (1.2.1)
|
||||||
|
prism (1.4.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (3.1.12)
|
rack (3.1.15)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
regexp_parser (2.10.0)
|
regexp_parser (2.10.0)
|
||||||
rubocop (1.74.0)
|
rubocop (1.75.7)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (~> 3.17.0.2)
|
||||||
lint_roller (~> 1.1.0)
|
lint_roller (~> 1.1.0)
|
||||||
|
@ -51,11 +52,12 @@ GEM
|
||||||
parser (>= 3.3.0.2)
|
parser (>= 3.3.0.2)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 2.9.3, < 3.0)
|
regexp_parser (>= 2.9.3, < 3.0)
|
||||||
rubocop-ast (>= 1.38.0, < 2.0)
|
rubocop-ast (>= 1.44.0, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 4.0)
|
unicode-display_width (>= 2.4.0, < 4.0)
|
||||||
rubocop-ast (1.38.1)
|
rubocop-ast (1.44.1)
|
||||||
parser (>= 3.3.1.0)
|
parser (>= 3.3.7.2)
|
||||||
|
prism (~> 1.4)
|
||||||
rubocop-capybara (2.22.1)
|
rubocop-capybara (2.22.1)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (~> 1.72, >= 1.72.1)
|
||||||
|
@ -71,13 +73,13 @@ GEM
|
||||||
rubocop-factory_bot (2.27.1)
|
rubocop-factory_bot (2.27.1)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (~> 1.72, >= 1.72.1)
|
||||||
rubocop-rails (2.30.3)
|
rubocop-rails (2.32.0)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.72.1, < 2.0)
|
rubocop (>= 1.75.0, < 2.0)
|
||||||
rubocop-ast (>= 1.38.0, < 2.0)
|
rubocop-ast (>= 1.44.0, < 2.0)
|
||||||
rubocop-rspec (3.5.0)
|
rubocop-rspec (3.6.0)
|
||||||
lint_roller (~> 1.1)
|
lint_roller (~> 1.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (~> 1.72, >= 1.72.1)
|
||||||
rubocop-rspec_rails (2.31.0)
|
rubocop-rspec_rails (2.31.0)
|
||||||
|
@ -104,4 +106,4 @@ DEPENDENCIES
|
||||||
translations-manager!
|
translations-manager!
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.6.6
|
2.6.9
|
||||||
|
|
|
@ -0,0 +1,399 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { concat } from "@ember/helper";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { equal } from "@ember/object/computed";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import { later } from "@ember/runloop";
|
||||||
|
import { service } from "@ember/service";
|
||||||
|
import { classNameBindings } from "@ember-decorators/component";
|
||||||
|
import { observes } from "@ember-decorators/object";
|
||||||
|
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import avatar from "discourse/helpers/avatar";
|
||||||
|
import icon from "discourse/helpers/d-icon";
|
||||||
|
import htmlSafe from "discourse/helpers/html-safe";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { setting } from "discourse/lib/computed";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatCurrency from "../helpers/format-currency";
|
||||||
|
|
||||||
|
const SIDEBAR_BODY_CLASS = "subscription-campaign-sidebar";
|
||||||
|
|
||||||
|
@classNameBindings("isGoalMet:goal-met")
|
||||||
|
export default class CampaignBanner extends Component {
|
||||||
|
@service router;
|
||||||
|
|
||||||
|
dismissed = false;
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_banner_shadow_color")
|
||||||
|
dropShadowColor;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_banner_bg_image")
|
||||||
|
backgroundImageUrl;
|
||||||
|
|
||||||
|
@equal(
|
||||||
|
"siteSettings.discourse_subscriptions_campaign_banner_location",
|
||||||
|
"Sidebar"
|
||||||
|
)
|
||||||
|
isSidebar;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_subscribers") subscribers;
|
||||||
|
|
||||||
|
@equal("siteSettings.discourse_subscriptions_campaign_type", "Subscribers")
|
||||||
|
subscriberGoal;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_currency") currency;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_amount_raised") amountRaised;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_goal") goalTarget;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_product") product;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_pricing_table_enabled") pricingTableEnabled;
|
||||||
|
|
||||||
|
@setting("discourse_subscriptions_campaign_show_contributors")
|
||||||
|
showContributors;
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(...arguments);
|
||||||
|
|
||||||
|
this.set("contributors", []);
|
||||||
|
|
||||||
|
// add background-image url to stylesheet
|
||||||
|
if (this.backgroundImageUrl) {
|
||||||
|
const backgroundUrl = `url(${this.backgroundImageUrl}`.replace(/\\/g, "");
|
||||||
|
if (
|
||||||
|
document.documentElement.style.getPropertyValue(
|
||||||
|
"--campaign-background-image"
|
||||||
|
) !== backgroundUrl
|
||||||
|
) {
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--campaign-background-image",
|
||||||
|
backgroundUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentUser && this.showContributors) {
|
||||||
|
return ajax("/s/contributors", { method: "get" }).then((result) => {
|
||||||
|
this.setProperties({
|
||||||
|
contributors: result,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
super.didInsertElement(...arguments);
|
||||||
|
if (this.isSidebar && this.shouldShow && !this.site.mobileView) {
|
||||||
|
document.body.classList.add(SIDEBAR_BODY_CLASS);
|
||||||
|
} else {
|
||||||
|
document.body.classList.remove(SIDEBAR_BODY_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// makes sure to only play animation once, & not repeat on reload
|
||||||
|
if (this.isGoalMet) {
|
||||||
|
const successAnimationKey = this.keyValueStore.get(
|
||||||
|
"campaign_success_animation"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!successAnimationKey) {
|
||||||
|
later(() => {
|
||||||
|
this.keyValueStore.set({
|
||||||
|
key: "campaign_success_animation",
|
||||||
|
value: Date.now(),
|
||||||
|
});
|
||||||
|
document.body.classList.add("success-animation-off");
|
||||||
|
}, 7000);
|
||||||
|
} else {
|
||||||
|
document.body.classList.add("success-animation-off");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
super.willDestroyElement(...arguments);
|
||||||
|
document.body.classList.remove(SIDEBAR_BODY_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("backgroundImageUrl")
|
||||||
|
bannerInfoStyle(backgroundImageUrl) {
|
||||||
|
if (!backgroundImageUrl) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `background-image: linear-gradient(
|
||||||
|
0deg,
|
||||||
|
rgba(var(--secondary-rgb), 0.75) 0%,
|
||||||
|
rgba(var(--secondary-rgb), 0.75) 100%),
|
||||||
|
var(--campaign-background-image);
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"router.currentRouteName",
|
||||||
|
"currentUser",
|
||||||
|
"siteSettings.discourse_subscriptions_campaign_enabled",
|
||||||
|
"visible"
|
||||||
|
)
|
||||||
|
shouldShow(currentRoute, currentUser, enabled, visible) {
|
||||||
|
// do not show on admin or subscriptions pages
|
||||||
|
const showOnRoute =
|
||||||
|
currentRoute !== "discovery.s" &&
|
||||||
|
!currentRoute.split(".")[0].includes("admin") &&
|
||||||
|
currentRoute.split(".")[0] !== "subscribe" &&
|
||||||
|
currentRoute.split(".")[0] !== "subscriptions";
|
||||||
|
|
||||||
|
if (!this.site.show_campaign_banner) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure not to render above main container when inside a topic
|
||||||
|
if (
|
||||||
|
this.connectorName === "above-main-container" &&
|
||||||
|
currentRoute.includes("topic")
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return showOnRoute && currentUser && enabled && visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
@observes("dismissed")
|
||||||
|
_updateBodyClasses() {
|
||||||
|
if (this.dismissed) {
|
||||||
|
document.body.classList.remove(SIDEBAR_BODY_CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("dismissed")
|
||||||
|
visible(dismissed) {
|
||||||
|
const dismissedBannerKey = this.keyValueStore.get(
|
||||||
|
"dismissed_campaign_banner"
|
||||||
|
);
|
||||||
|
const threeMonths = 2628000000 * 3;
|
||||||
|
|
||||||
|
const bannerDismissedTime = new Date(dismissedBannerKey);
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
return (
|
||||||
|
(!dismissedBannerKey || now - bannerDismissedTime > threeMonths) &&
|
||||||
|
!dismissed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
subscribeRoute() {
|
||||||
|
if (this.pricingTableEnabled) {
|
||||||
|
return "subscriptions";
|
||||||
|
}
|
||||||
|
return "subscribe";
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
isGoalMet() {
|
||||||
|
const currentVolume = this.subscriberGoal
|
||||||
|
? this.subscribers
|
||||||
|
: this.amountRaised;
|
||||||
|
return currentVolume >= this.goalTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
dismissBanner() {
|
||||||
|
this.set("dismissed", true);
|
||||||
|
this.keyValueStore.set({
|
||||||
|
key: "dismissed_campaign_banner",
|
||||||
|
value: Date.now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.shouldShow}}
|
||||||
|
<div
|
||||||
|
class="campaign-banner"
|
||||||
|
style={{htmlSafe (concat "box-shadow: 5px 5px #" this.dropShadowColor)}}
|
||||||
|
>
|
||||||
|
<DButton @icon="xmark" @action={{this.dismissBanner}} class="close" />
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="campaign-banner-info"
|
||||||
|
style={{htmlSafe this.bannerInfoStyle}}
|
||||||
|
>
|
||||||
|
{{#if this.isGoalMet}}
|
||||||
|
<h2 class="campaign-banner-info-header">
|
||||||
|
{{i18n "discourse_subscriptions.campaign.success_title"}}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="campaign-banner-info-description">
|
||||||
|
{{i18n "discourse_subscriptions.campaign.success_body"}}
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<h2 class="campaign-banner-info-header">
|
||||||
|
{{i18n "discourse_subscriptions.campaign.title"}}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="campaign-banner-info-description">
|
||||||
|
{{i18n "discourse_subscriptions.campaign.body"}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{#if this.product}}
|
||||||
|
<LinkTo
|
||||||
|
@route="subscribe.show"
|
||||||
|
@model={{this.product}}
|
||||||
|
@disabled={{this.product.subscribed}}
|
||||||
|
class="btn btn-primary campaign-banner-info-button"
|
||||||
|
>
|
||||||
|
{{icon "far-heart"}}
|
||||||
|
{{icon "heart" class="hover-heart"}}
|
||||||
|
{{i18n "discourse_subscriptions.campaign.button"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{else}}
|
||||||
|
<LinkTo
|
||||||
|
@route={{this.subscribeRoute}}
|
||||||
|
class="btn btn-primary campaign-banner-info-button"
|
||||||
|
>
|
||||||
|
{{icon "far-heart"}}
|
||||||
|
{{icon "heart" class="hover-heart"}}
|
||||||
|
{{i18n "discourse_subscriptions.campaign.button"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="campaign-banner-progress">
|
||||||
|
{{#if this.isGoalMet}}
|
||||||
|
<div class="fireworks">
|
||||||
|
<div class="before"></div>
|
||||||
|
<div class="after"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="campaign-banner-progress-success"></div>
|
||||||
|
|
||||||
|
{{#if this.subscriberGoal}}
|
||||||
|
<p class="campaign-banner-progress-description">
|
||||||
|
{{htmlSafe
|
||||||
|
(i18n
|
||||||
|
"discourse_subscriptions.campaign.goal_comparison"
|
||||||
|
current=this.subscribers
|
||||||
|
goal=this.goalTarget
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{i18n "discourse_subscriptions.campaign.subscribers"}}
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="campaign-banner-progress-description">
|
||||||
|
{{htmlSafe
|
||||||
|
(i18n
|
||||||
|
"discourse_subscriptions.campaign.goal_comparison"
|
||||||
|
current=(formatCurrency this.currency this.amountRaised)
|
||||||
|
goal=(formatCurrency this.currency this.goalTarget)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{i18n "discourse_subscriptions.campaign.raised"}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{#if this.showContributors}}
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@condition={{this.loading}}
|
||||||
|
@size="small"
|
||||||
|
>
|
||||||
|
<div class="campaign-banner-progress-users">
|
||||||
|
<p class="campaign-banner-progress-users-title">
|
||||||
|
<strong>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.campaign.recent_contributors"
|
||||||
|
}}
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="campaign-banner-progress-users-avatars">
|
||||||
|
{{#each this.contributors as |contributor|}}
|
||||||
|
{{avatar
|
||||||
|
contributor
|
||||||
|
avatarTemplatePath="avatar_template"
|
||||||
|
usernamePath="username"
|
||||||
|
namePath="name"
|
||||||
|
imageSize="small"
|
||||||
|
}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if this.subscriberGoal}}
|
||||||
|
<progress
|
||||||
|
class="campaign-banner-progress-bar"
|
||||||
|
value={{this.subscribers}}
|
||||||
|
max={{this.siteSettings.discourse_subscriptions_campaign_goal}}
|
||||||
|
></progress>
|
||||||
|
|
||||||
|
<p class="campaign-banner-progress-description">
|
||||||
|
{{htmlSafe
|
||||||
|
(i18n
|
||||||
|
"discourse_subscriptions.campaign.goal_comparison"
|
||||||
|
current=this.subscribers
|
||||||
|
goal=this.goalTarget
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{i18n "discourse_subscriptions.campaign.subscribers"}}
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<progress
|
||||||
|
class="campaign-banner-progress-bar"
|
||||||
|
value={{this.amountRaised}}
|
||||||
|
max={{this.siteSettings.discourse_subscriptions_campaign_goal}}
|
||||||
|
></progress>
|
||||||
|
|
||||||
|
<p class="campaign-banner-progress-description">
|
||||||
|
{{htmlSafe
|
||||||
|
(i18n
|
||||||
|
"discourse_subscriptions.campaign.goal_comparison"
|
||||||
|
current=(formatCurrency this.currency this.amountRaised)
|
||||||
|
goal=(formatCurrency this.currency this.goalTarget)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{i18n "discourse_subscriptions.campaign.raised"}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.showContributors}}
|
||||||
|
<ConditionalLoadingSpinner
|
||||||
|
@condition={{this.loading}}
|
||||||
|
@size="small"
|
||||||
|
>
|
||||||
|
<div class="campaign-banner-progress-users">
|
||||||
|
<p class="campaign-banner-progress-users-title">
|
||||||
|
<strong>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.campaign.recent_contributors"
|
||||||
|
}}
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="campaign-banner-progress-users-avatars">
|
||||||
|
{{#each this.contributors as |contributor|}}
|
||||||
|
{{avatar
|
||||||
|
contributor
|
||||||
|
avatarTemplatePath="avatar_template"
|
||||||
|
usernamePath="username"
|
||||||
|
namePath="name"
|
||||||
|
imageSize="small"
|
||||||
|
}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConditionalLoadingSpinner>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -1,176 +0,0 @@
|
||||||
{{#if this.shouldShow}}
|
|
||||||
<div
|
|
||||||
class="campaign-banner"
|
|
||||||
style={{html-safe (concat "box-shadow: 5px 5px #" this.dropShadowColor)}}
|
|
||||||
>
|
|
||||||
<DButton @icon="xmark" @action={{this.dismissBanner}} class="close" />
|
|
||||||
|
|
||||||
<div class="campaign-banner-info" style={{html-safe this.bannerInfoStyle}}>
|
|
||||||
{{#if this.isGoalMet}}
|
|
||||||
<h2 class="campaign-banner-info-header">
|
|
||||||
{{i18n "discourse_subscriptions.campaign.success_title"}}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p class="campaign-banner-info-description">
|
|
||||||
{{i18n "discourse_subscriptions.campaign.success_body"}}
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<h2 class="campaign-banner-info-header">
|
|
||||||
{{i18n "discourse_subscriptions.campaign.title"}}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<p class="campaign-banner-info-description">
|
|
||||||
{{i18n "discourse_subscriptions.campaign.body"}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#if this.product}}
|
|
||||||
<LinkTo
|
|
||||||
@route="subscribe.show"
|
|
||||||
@model={{this.product}}
|
|
||||||
@disabled={{this.product.subscribed}}
|
|
||||||
class="btn btn-primary campaign-banner-info-button"
|
|
||||||
>
|
|
||||||
{{d-icon "far-heart"}}
|
|
||||||
{{d-icon "heart" class="hover-heart"}}
|
|
||||||
{{i18n "discourse_subscriptions.campaign.button"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{else}}
|
|
||||||
<LinkTo
|
|
||||||
@route={{this.subscribeRoute}}
|
|
||||||
class="btn btn-primary campaign-banner-info-button"
|
|
||||||
>
|
|
||||||
{{d-icon "far-heart"}}
|
|
||||||
{{d-icon "heart" class="hover-heart"}}
|
|
||||||
{{i18n "discourse_subscriptions.campaign.button"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="campaign-banner-progress">
|
|
||||||
{{#if this.isGoalMet}}
|
|
||||||
<div class="fireworks">
|
|
||||||
<div class="before"></div>
|
|
||||||
<div class="after"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="campaign-banner-progress-success"></div>
|
|
||||||
|
|
||||||
{{#if this.subscriberGoal}}
|
|
||||||
<p class="campaign-banner-progress-description">
|
|
||||||
{{html-safe
|
|
||||||
(i18n
|
|
||||||
"discourse_subscriptions.campaign.goal_comparison"
|
|
||||||
current=this.subscribers
|
|
||||||
goal=this.goalTarget
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{i18n "discourse_subscriptions.campaign.subscribers"}}
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<p class="campaign-banner-progress-description">
|
|
||||||
{{html-safe
|
|
||||||
(i18n
|
|
||||||
"discourse_subscriptions.campaign.goal_comparison"
|
|
||||||
current=(format-currency this.currency this.amountRaised)
|
|
||||||
goal=(format-currency this.currency this.goalTarget)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{i18n "discourse_subscriptions.campaign.raised"}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#if this.showContributors}}
|
|
||||||
<ConditionalLoadingSpinner
|
|
||||||
@condition={{this.loading}}
|
|
||||||
@size="small"
|
|
||||||
>
|
|
||||||
<div class="campaign-banner-progress-users">
|
|
||||||
<p class="campaign-banner-progress-users-title">
|
|
||||||
<strong>
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.campaign.recent_contributors"
|
|
||||||
}}
|
|
||||||
</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="campaign-banner-progress-users-avatars">
|
|
||||||
{{#each this.contributors as |contributor|}}
|
|
||||||
{{avatar
|
|
||||||
contributor
|
|
||||||
avatarTemplatePath="avatar_template"
|
|
||||||
usernamePath="username"
|
|
||||||
namePath="name"
|
|
||||||
imageSize="small"
|
|
||||||
}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#if this.subscriberGoal}}
|
|
||||||
<progress
|
|
||||||
class="campaign-banner-progress-bar"
|
|
||||||
value={{this.subscribers}}
|
|
||||||
max={{this.siteSettings.discourse_subscriptions_campaign_goal}}
|
|
||||||
></progress>
|
|
||||||
|
|
||||||
<p class="campaign-banner-progress-description">
|
|
||||||
{{html-safe
|
|
||||||
(i18n
|
|
||||||
"discourse_subscriptions.campaign.goal_comparison"
|
|
||||||
current=this.subscribers
|
|
||||||
goal=this.goalTarget
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{i18n "discourse_subscriptions.campaign.subscribers"}}
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<progress
|
|
||||||
class="campaign-banner-progress-bar"
|
|
||||||
value={{this.amountRaised}}
|
|
||||||
max={{this.siteSettings.discourse_subscriptions_campaign_goal}}
|
|
||||||
></progress>
|
|
||||||
|
|
||||||
<p class="campaign-banner-progress-description">
|
|
||||||
{{html-safe
|
|
||||||
(i18n
|
|
||||||
"discourse_subscriptions.campaign.goal_comparison"
|
|
||||||
current=(format-currency this.currency this.amountRaised)
|
|
||||||
goal=(format-currency this.currency this.goalTarget)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{i18n "discourse_subscriptions.campaign.raised"}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.showContributors}}
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.loading}} @size="small">
|
|
||||||
<div class="campaign-banner-progress-users">
|
|
||||||
<p class="campaign-banner-progress-users-title">
|
|
||||||
<strong>
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.campaign.recent_contributors"
|
|
||||||
}}
|
|
||||||
</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="campaign-banner-progress-users-avatars">
|
|
||||||
{{#each this.contributors as |contributor|}}
|
|
||||||
{{avatar
|
|
||||||
contributor
|
|
||||||
avatarTemplatePath="avatar_template"
|
|
||||||
usernamePath="username"
|
|
||||||
namePath="name"
|
|
||||||
imageSize="small"
|
|
||||||
}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ConditionalLoadingSpinner>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -1,205 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { equal } from "@ember/object/computed";
|
|
||||||
import { later } from "@ember/runloop";
|
|
||||||
import { service } from "@ember/service";
|
|
||||||
import { classNameBindings } from "@ember-decorators/component";
|
|
||||||
import { observes } from "@ember-decorators/object";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import { setting } from "discourse/lib/computed";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
|
|
||||||
const SIDEBAR_BODY_CLASS = "subscription-campaign-sidebar";
|
|
||||||
|
|
||||||
@classNameBindings("isGoalMet:goal-met")
|
|
||||||
export default class CampaignBanner extends Component {
|
|
||||||
@service router;
|
|
||||||
|
|
||||||
dismissed = false;
|
|
||||||
loading = false;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_banner_shadow_color")
|
|
||||||
dropShadowColor;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_banner_bg_image")
|
|
||||||
backgroundImageUrl;
|
|
||||||
|
|
||||||
@equal(
|
|
||||||
"siteSettings.discourse_subscriptions_campaign_banner_location",
|
|
||||||
"Sidebar"
|
|
||||||
)
|
|
||||||
isSidebar;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_subscribers") subscribers;
|
|
||||||
|
|
||||||
@equal("siteSettings.discourse_subscriptions_campaign_type", "Subscribers")
|
|
||||||
subscriberGoal;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_currency") currency;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_amount_raised") amountRaised;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_goal") goalTarget;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_product") product;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_pricing_table_enabled") pricingTableEnabled;
|
|
||||||
|
|
||||||
@setting("discourse_subscriptions_campaign_show_contributors")
|
|
||||||
showContributors;
|
|
||||||
|
|
||||||
init() {
|
|
||||||
super.init(...arguments);
|
|
||||||
|
|
||||||
this.set("contributors", []);
|
|
||||||
|
|
||||||
// add background-image url to stylesheet
|
|
||||||
if (this.backgroundImageUrl) {
|
|
||||||
const backgroundUrl = `url(${this.backgroundImageUrl}`.replace(/\\/g, "");
|
|
||||||
if (
|
|
||||||
document.documentElement.style.getPropertyValue(
|
|
||||||
"--campaign-background-image"
|
|
||||||
) !== backgroundUrl
|
|
||||||
) {
|
|
||||||
document.documentElement.style.setProperty(
|
|
||||||
"--campaign-background-image",
|
|
||||||
backgroundUrl
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.currentUser && this.showContributors) {
|
|
||||||
return ajax("/s/contributors", { method: "get" }).then((result) => {
|
|
||||||
this.setProperties({
|
|
||||||
contributors: result,
|
|
||||||
loading: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
super.didInsertElement(...arguments);
|
|
||||||
if (this.isSidebar && this.shouldShow && !this.site.mobileView) {
|
|
||||||
document.body.classList.add(SIDEBAR_BODY_CLASS);
|
|
||||||
} else {
|
|
||||||
document.body.classList.remove(SIDEBAR_BODY_CLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes sure to only play animation once, & not repeat on reload
|
|
||||||
if (this.isGoalMet) {
|
|
||||||
const successAnimationKey = this.keyValueStore.get(
|
|
||||||
"campaign_success_animation"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!successAnimationKey) {
|
|
||||||
later(() => {
|
|
||||||
this.keyValueStore.set({
|
|
||||||
key: "campaign_success_animation",
|
|
||||||
value: Date.now(),
|
|
||||||
});
|
|
||||||
document.body.classList.add("success-animation-off");
|
|
||||||
}, 7000);
|
|
||||||
} else {
|
|
||||||
document.body.classList.add("success-animation-off");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
super.willDestroyElement(...arguments);
|
|
||||||
document.body.classList.remove(SIDEBAR_BODY_CLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed("backgroundImageUrl")
|
|
||||||
bannerInfoStyle(backgroundImageUrl) {
|
|
||||||
if (!backgroundImageUrl) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return `background-image: linear-gradient(
|
|
||||||
0deg,
|
|
||||||
rgba(var(--secondary-rgb), 0.75) 0%,
|
|
||||||
rgba(var(--secondary-rgb), 0.75) 100%),
|
|
||||||
var(--campaign-background-image);
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;`;
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed(
|
|
||||||
"router.currentRouteName",
|
|
||||||
"currentUser",
|
|
||||||
"siteSettings.discourse_subscriptions_campaign_enabled",
|
|
||||||
"visible"
|
|
||||||
)
|
|
||||||
shouldShow(currentRoute, currentUser, enabled, visible) {
|
|
||||||
// do not show on admin or subscriptions pages
|
|
||||||
const showOnRoute =
|
|
||||||
currentRoute !== "discovery.s" &&
|
|
||||||
!currentRoute.split(".")[0].includes("admin") &&
|
|
||||||
currentRoute.split(".")[0] !== "subscribe" &&
|
|
||||||
currentRoute.split(".")[0] !== "subscriptions";
|
|
||||||
|
|
||||||
if (!this.site.show_campaign_banner) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure not to render above main container when inside a topic
|
|
||||||
if (
|
|
||||||
this.connectorName === "above-main-container" &&
|
|
||||||
currentRoute.includes("topic")
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return showOnRoute && currentUser && enabled && visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
@observes("dismissed")
|
|
||||||
_updateBodyClasses() {
|
|
||||||
if (this.dismissed) {
|
|
||||||
document.body.classList.remove(SIDEBAR_BODY_CLASS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed("dismissed")
|
|
||||||
visible(dismissed) {
|
|
||||||
const dismissedBannerKey = this.keyValueStore.get(
|
|
||||||
"dismissed_campaign_banner"
|
|
||||||
);
|
|
||||||
const threeMonths = 2628000000 * 3;
|
|
||||||
|
|
||||||
const bannerDismissedTime = new Date(dismissedBannerKey);
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
return (
|
|
||||||
(!dismissedBannerKey || now - bannerDismissedTime > threeMonths) &&
|
|
||||||
!dismissed
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
subscribeRoute() {
|
|
||||||
if (this.pricingTableEnabled) {
|
|
||||||
return "subscriptions";
|
|
||||||
}
|
|
||||||
return "subscribe";
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
isGoalMet() {
|
|
||||||
const currentVolume = this.subscriberGoal
|
|
||||||
? this.subscribers
|
|
||||||
: this.amountRaised;
|
|
||||||
return currentVolume >= this.goalTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
dismissBanner() {
|
|
||||||
this.set("dismissed", true);
|
|
||||||
this.keyValueStore.set({
|
|
||||||
key: "dismissed_campaign_banner",
|
|
||||||
value: Date.now(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import Component, { Input } from "@ember/component";
|
||||||
|
import { fn } from "@ember/helper";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import ComboBox from "select-kit/components/combo-box";
|
||||||
|
|
||||||
|
export default class CreateCouponForm extends Component {
|
||||||
|
discountType = "amount";
|
||||||
|
discount = null;
|
||||||
|
promoCode = null;
|
||||||
|
active = false;
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
discountTypes() {
|
||||||
|
return [
|
||||||
|
{ id: "amount", name: "Amount" },
|
||||||
|
{ id: "percent", name: "Percent" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
createNewCoupon() {
|
||||||
|
const createParams = {
|
||||||
|
promo: this.promoCode,
|
||||||
|
discount_type: this.discountType,
|
||||||
|
discount: this.discount,
|
||||||
|
active: this.active,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.create(createParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
cancelCreate() {
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="create-coupon-form">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<p>
|
||||||
|
<label for="promo_code">
|
||||||
|
{{i18n "discourse_subscriptions.admin.coupons.promo_code"}}
|
||||||
|
</label>
|
||||||
|
<Input @type="text" name="promo_code" @value={{this.promoCode}} />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="amount">
|
||||||
|
{{i18n "discourse_subscriptions.admin.coupons.discount"}}
|
||||||
|
</label>
|
||||||
|
<ComboBox
|
||||||
|
@content={{this.discountTypes}}
|
||||||
|
@value={{this.discountType}}
|
||||||
|
@onChange={{fn (mut this.discountType)}}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
class="discount-amount"
|
||||||
|
@type="text"
|
||||||
|
name="amount"
|
||||||
|
@value={{this.discount}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="active">
|
||||||
|
{{i18n "discourse_subscriptions.admin.coupons.active"}}
|
||||||
|
</label>
|
||||||
|
<Input @type="checkbox" name="active" @checked={{this.active}} />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@action={{this.createNewCoupon}}
|
||||||
|
@label="discourse_subscriptions.admin.coupons.create"
|
||||||
|
@title="discourse_subscriptions.admin.coupons.create"
|
||||||
|
@icon="plus"
|
||||||
|
class="btn-primary btn btn-icon"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@action={{this.cancelCreate}}
|
||||||
|
label="cancel"
|
||||||
|
@title="cancel"
|
||||||
|
@icon="xmark"
|
||||||
|
class="btn btn-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
<div class="create-coupon-form">
|
|
||||||
<form class="form-horizontal">
|
|
||||||
<p>
|
|
||||||
<label for="promo_code">
|
|
||||||
{{i18n "discourse_subscriptions.admin.coupons.promo_code"}}
|
|
||||||
</label>
|
|
||||||
<Input @type="text" name="promo_code" @value={{this.promoCode}} />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="amount">
|
|
||||||
{{i18n "discourse_subscriptions.admin.coupons.discount"}}
|
|
||||||
</label>
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.discountTypes}}
|
|
||||||
@value={{this.discountType}}
|
|
||||||
@onChange={{action (mut this.discountType)}}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
class="discount-amount"
|
|
||||||
@type="text"
|
|
||||||
name="amount"
|
|
||||||
@value={{this.discount}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="active">
|
|
||||||
{{i18n "discourse_subscriptions.admin.coupons.active"}}
|
|
||||||
</label>
|
|
||||||
<Input @type="checkbox" name="active" @checked={{this.active}} />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@action={{action "createNewCoupon"}}
|
|
||||||
@label="discourse_subscriptions.admin.coupons.create"
|
|
||||||
@title="discourse_subscriptions.admin.coupons.create"
|
|
||||||
@icon="plus"
|
|
||||||
class="btn-primary btn btn-icon"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@action={{action "cancelCreate"}}
|
|
||||||
label="cancel"
|
|
||||||
@title="cancel"
|
|
||||||
@icon="xmark"
|
|
||||||
class="btn btn-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
|
@ -1,35 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
|
|
||||||
export default class CreateCouponForm extends Component {
|
|
||||||
discountType = "amount";
|
|
||||||
discount = null;
|
|
||||||
promoCode = null;
|
|
||||||
active = false;
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
discountTypes() {
|
|
||||||
return [
|
|
||||||
{ id: "amount", name: "Amount" },
|
|
||||||
{ id: "percent", name: "Percent" },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
createNewCoupon() {
|
|
||||||
const createParams = {
|
|
||||||
promo: this.promoCode,
|
|
||||||
discount_type: this.discountType,
|
|
||||||
discount: this.discount,
|
|
||||||
active: this.active,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.create(createParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
cancelCreate() {
|
|
||||||
this.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import routeAction from "discourse/helpers/route-action";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
const LoginRequired = <template>
|
||||||
|
<h3>{{i18n "discourse_subscriptions.subscribe.unauthenticated"}}</h3>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@label="log_in"
|
||||||
|
@action={{routeAction "showLogin"}}
|
||||||
|
@icon="user"
|
||||||
|
class="btn btn-primary login-required subscriptions"
|
||||||
|
/>
|
||||||
|
</template>;
|
||||||
|
|
||||||
|
export default LoginRequired;
|
|
@ -1,8 +0,0 @@
|
||||||
<h3>{{i18n "discourse_subscriptions.subscribe.unauthenticated"}}</h3>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@label="log_in"
|
|
||||||
@action={{route-action "showLogin"}}
|
|
||||||
@icon="user"
|
|
||||||
class="btn btn-primary login-required subscriptions"
|
|
||||||
/>
|
|
|
@ -1,6 +1,8 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import PaymentPlan from "./payment-plan";
|
||||||
|
|
||||||
export default class PaymentOptions extends Component {
|
export default class PaymentOptions extends Component {
|
||||||
@discourseComputed("plans")
|
@discourseComputed("plans")
|
||||||
|
@ -21,4 +23,20 @@ export default class PaymentOptions extends Component {
|
||||||
clickPlan(plan) {
|
clickPlan(plan) {
|
||||||
this.set("selectedPlan", plan.id);
|
this.set("selectedPlan", plan.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<p>
|
||||||
|
{{i18n "discourse_subscriptions.plans.select"}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="subscribe-buttons">
|
||||||
|
{{#each this.orderedPlans as |plan|}}
|
||||||
|
<PaymentPlan
|
||||||
|
@plan={{plan}}
|
||||||
|
@selectedPlan={{this.selectedPlan}}
|
||||||
|
@clickPlan={{this.clickPlan}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
}
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
<p>
|
|
||||||
{{i18n "discourse_subscriptions.plans.select"}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="subscribe-buttons">
|
|
||||||
{{#each this.orderedPlans as |plan|}}
|
|
||||||
<PaymentPlan
|
|
||||||
@plan={{plan}}
|
|
||||||
@selectedPlan={{this.selectedPlan}}
|
|
||||||
@clickPlan={{action "clickPlan"}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { concat } from "@ember/helper";
|
||||||
|
import { action } from "@ember/object";
|
||||||
|
import { tagName } from "@ember-decorators/component";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import concatClass from "discourse/helpers/concat-class";
|
||||||
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatCurrency from "../helpers/format-currency";
|
||||||
|
|
||||||
|
const RECURRING = "recurring";
|
||||||
|
|
||||||
|
@tagName("")
|
||||||
|
export default class PaymentPlan extends Component {
|
||||||
|
@discourseComputed("selectedPlan")
|
||||||
|
selectedClass(planId) {
|
||||||
|
return planId === this.plan.id ? "btn-primary" : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@discourseComputed("plan.type")
|
||||||
|
recurringPlan(type) {
|
||||||
|
return type === RECURRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
planClick() {
|
||||||
|
this.clickPlan(this.plan);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<DButton
|
||||||
|
@action={{this.planClick}}
|
||||||
|
class={{concatClass
|
||||||
|
"btn-discourse-subscriptions-subscribe"
|
||||||
|
this.selectedClass
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span class="interval">
|
||||||
|
{{#if this.recurringPlan}}
|
||||||
|
{{i18n
|
||||||
|
(concat
|
||||||
|
"discourse_subscriptions.plans.interval.adverb."
|
||||||
|
this.plan.recurring.interval
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{else}}
|
||||||
|
{{i18n "discourse_subscriptions.one_time_payment"}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="amount">
|
||||||
|
{{formatCurrency this.plan.currency this.plan.amountDollars}}
|
||||||
|
</span>
|
||||||
|
</DButton>
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
<DButton
|
|
||||||
@action={{action "planClick"}}
|
|
||||||
class={{concat-class
|
|
||||||
"btn-discourse-subscriptions-subscribe"
|
|
||||||
this.selectedClass
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span class="interval">
|
|
||||||
{{#if this.recurringPlan}}
|
|
||||||
{{i18n
|
|
||||||
(concat
|
|
||||||
"discourse_subscriptions.plans.interval.adverb."
|
|
||||||
this.plan.recurring.interval
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
{{else}}
|
|
||||||
{{i18n "discourse_subscriptions.one_time_payment"}}
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="amount">
|
|
||||||
{{format-currency this.plan.currency this.plan.amountDollars}}
|
|
||||||
</span>
|
|
||||||
</DButton>
|
|
|
@ -1,25 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import { tagName } from "@ember-decorators/component";
|
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
|
||||||
|
|
||||||
const RECURRING = "recurring";
|
|
||||||
|
|
||||||
@tagName("")
|
|
||||||
export default class PaymentPlan extends Component {
|
|
||||||
@discourseComputed("selectedPlan")
|
|
||||||
selectedClass(planId) {
|
|
||||||
return planId === this.plan.id ? "btn-primary" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed("plan.type")
|
|
||||||
recurringPlan(type) {
|
|
||||||
return type === RECURRING;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
planClick() {
|
|
||||||
this.clickPlan(this.plan);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import { classNames } from "@ember-decorators/component";
|
||||||
|
import htmlSafe from "discourse/helpers/html-safe";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
@classNames("product")
|
||||||
|
export default class ProductItem extends Component {
|
||||||
|
<template>
|
||||||
|
<h2>{{this.product.name}}</h2>
|
||||||
|
|
||||||
|
<p class="product-description">
|
||||||
|
{{htmlSafe this.product.description}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{#if this.isLoggedIn}}
|
||||||
|
<div class="product-purchase">
|
||||||
|
{{#if this.product.repurchaseable}}
|
||||||
|
<LinkTo
|
||||||
|
@route="subscribe.show"
|
||||||
|
@model={{this.product.id}}
|
||||||
|
class="btn btn-primary"
|
||||||
|
>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.title"}}
|
||||||
|
</LinkTo>
|
||||||
|
|
||||||
|
{{#if this.product.subscribed}}
|
||||||
|
<LinkTo
|
||||||
|
@route="user.billing.subscriptions"
|
||||||
|
@model={{this.currentUser.username}}
|
||||||
|
class="billing-link"
|
||||||
|
>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.view_past"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if this.product.subscribed}}
|
||||||
|
<span class="purchased">
|
||||||
|
✓
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.purchased"}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<LinkTo
|
||||||
|
@route="user.billing.subscriptions"
|
||||||
|
@model={{this.currentUser.username}}
|
||||||
|
class="billing-link"
|
||||||
|
>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.go_to_billing"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{else}}
|
||||||
|
<LinkTo
|
||||||
|
@route="subscribe.show"
|
||||||
|
@model={{this.product.id}}
|
||||||
|
@disabled={{this.product.subscribed}}
|
||||||
|
class="btn btn-primary"
|
||||||
|
>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.title"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -1,53 +0,0 @@
|
||||||
<h2>{{this.product.name}}</h2>
|
|
||||||
|
|
||||||
<p class="product-description">
|
|
||||||
{{html-safe this.product.description}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#if this.isLoggedIn}}
|
|
||||||
<div class="product-purchase">
|
|
||||||
{{#if this.product.repurchaseable}}
|
|
||||||
<LinkTo
|
|
||||||
@route="subscribe.show"
|
|
||||||
@model={{this.product.id}}
|
|
||||||
class="btn btn-primary"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.title"}}
|
|
||||||
</LinkTo>
|
|
||||||
|
|
||||||
{{#if this.product.subscribed}}
|
|
||||||
<LinkTo
|
|
||||||
@route="user.billing.subscriptions"
|
|
||||||
@model={{this.currentUser.username}}
|
|
||||||
class="billing-link"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.view_past"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#if this.product.subscribed}}
|
|
||||||
<span class="purchased">
|
|
||||||
✓
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.purchased"}}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<LinkTo
|
|
||||||
@route="user.billing.subscriptions"
|
|
||||||
@model={{this.currentUser.username}}
|
|
||||||
class="billing-link"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.go_to_billing"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{else}}
|
|
||||||
<LinkTo
|
|
||||||
@route="subscribe.show"
|
|
||||||
@model={{this.product.id}}
|
|
||||||
@disabled={{this.product.subscribed}}
|
|
||||||
class="btn btn-primary"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.title"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -1,5 +0,0 @@
|
||||||
import Component from "@ember/component";
|
|
||||||
import { classNames } from "@ember-decorators/component";
|
|
||||||
|
|
||||||
@classNames("product")
|
|
||||||
export default class ProductItem extends Component {}
|
|
|
@ -2,6 +2,8 @@ import Component from "@ember/component";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
import { classNames } from "@ember-decorators/component";
|
import { classNames } from "@ember-decorators/component";
|
||||||
import discourseComputed from "discourse/lib/decorators";
|
import discourseComputed from "discourse/lib/decorators";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import ProductItem from "./product-item";
|
||||||
|
|
||||||
@classNames("product-list")
|
@classNames("product-list")
|
||||||
export default class ProductList extends Component {
|
export default class ProductList extends Component {
|
||||||
|
@ -9,4 +11,14 @@ export default class ProductList extends Component {
|
||||||
emptyProducts(products) {
|
emptyProducts(products) {
|
||||||
return isEmpty(products);
|
return isEmpty(products);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
{{#if this.emptyProducts}}
|
||||||
|
<p>{{i18n "discourse_subscriptions.subscribe.no_products"}}</p>
|
||||||
|
{{else}}
|
||||||
|
{{#each this.products as |product|}}
|
||||||
|
<ProductItem @product={{product}} @isLoggedIn={{this.isLoggedIn}} />
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
}
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
{{#if this.emptyProducts}}
|
|
||||||
<p>{{i18n "discourse_subscriptions.subscribe.no_products"}}</p>
|
|
||||||
{{else}}
|
|
||||||
{{#each this.products as |product|}}
|
|
||||||
<ProductItem @product={{product}} @isLoggedIn={{this.isLoggedIn}} />
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
|
@ -28,4 +28,8 @@ export default class SubscribeCard extends Component {
|
||||||
didDestroyElement() {
|
didDestroyElement() {
|
||||||
super.didDestroyElement(...arguments);
|
super.didDestroyElement(...arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="card-element"></div>
|
||||||
|
</template>
|
||||||
}
|
}
|
|
@ -1 +0,0 @@
|
||||||
<div id="card-element"></div>
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { classNames, tagName } from "@ember-decorators/component";
|
||||||
|
import CampaignBanner from "../../components/campaign-banner";
|
||||||
|
|
||||||
|
@tagName("div")
|
||||||
|
@classNames("above-main-container-outlet", "subscriptions-campaign")
|
||||||
|
export default class SubscriptionsCampaign extends Component {
|
||||||
|
static shouldRender(args, context) {
|
||||||
|
const { siteSettings } = context;
|
||||||
|
const mobileView = context.site.mobileView;
|
||||||
|
const bannerLocation =
|
||||||
|
siteSettings.discourse_subscriptions_campaign_banner_location;
|
||||||
|
return (
|
||||||
|
bannerLocation === "Top" || (bannerLocation === "Sidebar" && mobileView)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
<template><CampaignBanner @connectorName="above-main-container" /></template>
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
<CampaignBanner @connectorName="above-main-container" />
|
|
|
@ -1,12 +0,0 @@
|
||||||
export default {
|
|
||||||
shouldRender(args, component) {
|
|
||||||
const { siteSettings } = component;
|
|
||||||
const mobileView = component.site.mobileView;
|
|
||||||
const bannerLocation =
|
|
||||||
siteSettings.discourse_subscriptions_campaign_banner_location;
|
|
||||||
|
|
||||||
return (
|
|
||||||
bannerLocation === "Top" || (bannerLocation === "Sidebar" && mobileView)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { classNames, tagName } from "@ember-decorators/component";
|
||||||
|
import CampaignBanner from "../../components/campaign-banner";
|
||||||
|
|
||||||
|
@tagName("span")
|
||||||
|
@classNames(
|
||||||
|
"after-topic-footer-buttons-outlet",
|
||||||
|
"subscriptions-campaign-topic-footer"
|
||||||
|
)
|
||||||
|
export default class SubscriptionsCampaignTopicFooter extends Component {
|
||||||
|
static shouldRender(args, context) {
|
||||||
|
const { siteSettings } = context;
|
||||||
|
const bannerLocation =
|
||||||
|
siteSettings.discourse_subscriptions_campaign_banner_location;
|
||||||
|
return bannerLocation === "Top" || bannerLocation === "Sidebar";
|
||||||
|
}
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CampaignBanner @connectorName="after-topic-footer-buttons" />
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
<CampaignBanner @connectorName="after-topic-footer-buttons" />
|
|
|
@ -1,9 +0,0 @@
|
||||||
export default {
|
|
||||||
shouldRender(args, component) {
|
|
||||||
const { siteSettings } = component;
|
|
||||||
const bannerLocation =
|
|
||||||
siteSettings.discourse_subscriptions_campaign_banner_location;
|
|
||||||
|
|
||||||
return bannerLocation === "Top" || bannerLocation === "Sidebar";
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { classNames, tagName } from "@ember-decorators/component";
|
||||||
|
import CampaignBanner from "../../components/campaign-banner";
|
||||||
|
|
||||||
|
@tagName("div")
|
||||||
|
@classNames("before-topic-list-outlet", "subscriptions-campaign-sidebar")
|
||||||
|
export default class SubscriptionsCampaignSidebar extends Component {
|
||||||
|
static shouldRender(args, context) {
|
||||||
|
const { siteSettings } = context;
|
||||||
|
const mobileView = context.site.mobileView;
|
||||||
|
const bannerLocation =
|
||||||
|
siteSettings.discourse_subscriptions_campaign_banner_location;
|
||||||
|
return bannerLocation === "Sidebar" && !mobileView;
|
||||||
|
}
|
||||||
|
|
||||||
|
<template><CampaignBanner @connectorName="before-topic-list" /></template>
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
<CampaignBanner @connectorName="before-topic-list" />
|
|
|
@ -1,10 +0,0 @@
|
||||||
export default {
|
|
||||||
shouldRender(args, component) {
|
|
||||||
const { siteSettings } = component;
|
|
||||||
const mobileView = component.site.mobileView;
|
|
||||||
const bannerLocation =
|
|
||||||
siteSettings.discourse_subscriptions_campaign_banner_location;
|
|
||||||
|
|
||||||
return bannerLocation === "Sidebar" && !mobileView;
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import { classNames, tagName } from "@ember-decorators/component";
|
||||||
|
import icon from "discourse/helpers/d-icon";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import userViewingSelf from "../../helpers/user-viewing-self";
|
||||||
|
|
||||||
|
@tagName("li")
|
||||||
|
@classNames("user-main-nav-outlet", "billing")
|
||||||
|
export default class Billing extends Component {
|
||||||
|
<template>
|
||||||
|
{{#if (userViewingSelf this.model)}}
|
||||||
|
<LinkTo @route="user.billing">
|
||||||
|
{{icon "far-credit-card"}}
|
||||||
|
{{i18n "discourse_subscriptions.navigation.billing"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
}
|
|
@ -1,6 +0,0 @@
|
||||||
{{#if (user-viewing-self this.model)}}
|
|
||||||
<LinkTo @route="user.billing">
|
|
||||||
{{d-icon "far-credit-card"}}
|
|
||||||
{{i18n "discourse_subscriptions.navigation.billing"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { Input } from "@ember/component";
|
||||||
|
import { fn } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import CreateCouponForm from "../../components/create-coupon-form";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{#if @controller.model.unconfigured}}
|
||||||
|
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
||||||
|
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
{{#if @controller.model}}
|
||||||
|
<table class="table discourse-patrons-table">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.coupons.code"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.coupons.discount"}}</th>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.admin.coupons.times_redeemed"
|
||||||
|
}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.coupons.active"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.coupons.actions"}}</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model as |coupon|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{coupon.code}}</td>
|
||||||
|
<td>{{coupon.discount}}</td>
|
||||||
|
<td>{{coupon.times_redeemed}}</td>
|
||||||
|
<td>
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
@checked={{coupon.active}}
|
||||||
|
{{on "click" (fn @controller.toggleActive coupon)}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<DButton
|
||||||
|
@action={{fn @controller.deleteCoupon coupon}}
|
||||||
|
@icon="trash-can"
|
||||||
|
class="btn-danger btn btn-icon btn-no-text"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless @controller.creating}}
|
||||||
|
<DButton
|
||||||
|
@action={{@controller.openCreateForm}}
|
||||||
|
@label="discourse_subscriptions.admin.coupons.create"
|
||||||
|
@title="discourse_subscriptions.admin.coupons.create"
|
||||||
|
@icon="plus"
|
||||||
|
class="btn btn-icon btn-primary create-coupon"
|
||||||
|
/>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if @controller.creating}}
|
||||||
|
<CreateCouponForm
|
||||||
|
@cancel={{@controller.closeCreateForm}}
|
||||||
|
@create={{@controller.createNewCoupon}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,61 +0,0 @@
|
||||||
{{#if this.model.unconfigured}}
|
|
||||||
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
|
||||||
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
{{#if this.model}}
|
|
||||||
<table class="table discourse-patrons-table">
|
|
||||||
<thead>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.coupons.code"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.coupons.discount"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.coupons.times_redeemed"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.coupons.active"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.coupons.actions"}}</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model as |coupon|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{coupon.code}}</td>
|
|
||||||
<td>{{coupon.discount}}</td>
|
|
||||||
<td>{{coupon.times_redeemed}}</td>
|
|
||||||
<td>
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
@checked={{coupon.active}}
|
|
||||||
{{on "click" (action "toggleActive" coupon)}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<DButton
|
|
||||||
@action={{action "deleteCoupon"}}
|
|
||||||
@actionParam={{coupon}}
|
|
||||||
@icon="trash-can"
|
|
||||||
class="btn-danger btn btn-icon btn-no-text"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#unless this.creating}}
|
|
||||||
<DButton
|
|
||||||
@action={{action "openCreateForm"}}
|
|
||||||
@label="discourse_subscriptions.admin.coupons.create"
|
|
||||||
@title="discourse_subscriptions.admin.coupons.create"
|
|
||||||
@icon="plus"
|
|
||||||
class="btn btn-icon btn-primary create-coupon"
|
|
||||||
/>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if this.creating}}
|
|
||||||
<CreateCouponForm
|
|
||||||
@cancel={{action "closeCreateForm"}}
|
|
||||||
@create={{action "createNewCoupon"}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { array, fn } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import LoadMore from "discourse/components/load-more";
|
||||||
|
import formatDuration from "discourse/helpers/format-duration";
|
||||||
|
import htmlSafe from "discourse/helpers/html-safe";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<h3>{{i18n "discourse_subscriptions.admin.dashboard.title"}}</h3>
|
||||||
|
|
||||||
|
<LoadMore
|
||||||
|
@selector=".discourse-patrons-table tr"
|
||||||
|
@action={{@controller.loadMore}}
|
||||||
|
>
|
||||||
|
{{#if @controller.model}}
|
||||||
|
<table class="table discourse-patrons-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.dashboard.table.head.user"
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.dashboard.table.head.payment_intent"
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.dashboard.table.head.receipt_email"
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
{{on "click" (fn @controller.orderPayments "created_at")}}
|
||||||
|
role="button"
|
||||||
|
class="sortable"
|
||||||
|
>
|
||||||
|
{{i18n "created"}}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
{{on "click" (fn @controller.orderPayments "amount")}}
|
||||||
|
role="button"
|
||||||
|
class="sortable amount"
|
||||||
|
>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.dashboard.table.head.amount"
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model as |payment|}}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<LinkTo
|
||||||
|
@route="adminUser.index"
|
||||||
|
@models={{array payment.user_id payment.username}}
|
||||||
|
>
|
||||||
|
{{payment.username}}
|
||||||
|
</LinkTo>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<LinkTo
|
||||||
|
@route="patrons.show"
|
||||||
|
@model={{payment.payment_intent_id}}
|
||||||
|
>
|
||||||
|
{{htmlSafe payment.payment_intent_id}}
|
||||||
|
</LinkTo>
|
||||||
|
</td>
|
||||||
|
<td>{{payment.receipt_email}}</td>
|
||||||
|
<td>{{htmlSafe (formatDuration payment.created_at_age)}}</td>
|
||||||
|
<td class="amount">{{payment.amount_currency}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
</LoadMore>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,64 +0,0 @@
|
||||||
<h3>{{i18n "discourse_subscriptions.admin.dashboard.title"}}</h3>
|
|
||||||
|
|
||||||
<LoadMore @selector=".discourse-patrons-table tr" @action={{action "loadMore"}}>
|
|
||||||
{{#if this.model}}
|
|
||||||
<table class="table discourse-patrons-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
{{i18n "discourse_subscriptions.admin.dashboard.table.head.user"}}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.admin.dashboard.table.head.payment_intent"
|
|
||||||
}}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.admin.dashboard.table.head.receipt_email"
|
|
||||||
}}
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
role="button"
|
|
||||||
onclick={{action "orderPayments" "created_at"}}
|
|
||||||
class="sortable"
|
|
||||||
>
|
|
||||||
{{i18n "created"}}
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
role="button"
|
|
||||||
onclick={{action "orderPayments" "amount"}}
|
|
||||||
class="sortable amount"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.admin.dashboard.table.head.amount"}}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model as |payment|}}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<LinkTo
|
|
||||||
@route="adminUser.index"
|
|
||||||
@models={{array payment.user_id payment.username}}
|
|
||||||
>
|
|
||||||
{{payment.username}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<LinkTo
|
|
||||||
@route="patrons.show"
|
|
||||||
@model={{payment.payment_intent_id}}
|
|
||||||
>
|
|
||||||
{{html-safe payment.payment_intent_id}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
<td>{{payment.receipt_email}}</td>
|
|
||||||
<td>{{html-safe (format-duration payment.created_at_age)}}</td>
|
|
||||||
<td class="amount">{{payment.amount_currency}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{/if}}
|
|
||||||
</LoadMore>
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { fn } from "@ember/helper";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import routeAction from "discourse/helpers/route-action";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<table class="table discourse-patrons-table">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.plans.plan.plan_id"}}</th>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.admin.plans.plan.nickname.title"
|
||||||
|
}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.plans.plan.interval"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.plans.plan.amount"}}</th>
|
||||||
|
<th></th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model as |plan|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{plan.id}}</td>
|
||||||
|
<td>{{plan.nickname}}</td>
|
||||||
|
<td>{{plan.interval}}</td>
|
||||||
|
<td>{{plan.unit_amount}}</td>
|
||||||
|
<td class="td-right">
|
||||||
|
<DButton
|
||||||
|
@action={{fn @controller.editPlan plan.id}}
|
||||||
|
@icon="far-pen-to-square"
|
||||||
|
class="btn no-text btn-icon"
|
||||||
|
/>
|
||||||
|
<DButton
|
||||||
|
@action={{routeAction "destroyPlan"}}
|
||||||
|
@actionParam={{plan}}
|
||||||
|
@icon="trash-can"
|
||||||
|
class="btn-danger btn no-text btn-icon"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,32 +0,0 @@
|
||||||
<table class="table discourse-patrons-table">
|
|
||||||
<thead>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.plan_id"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.nickname.title"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.interval"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.amount"}}</th>
|
|
||||||
<th></th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model as |plan|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{plan.id}}</td>
|
|
||||||
<td>{{plan.nickname}}</td>
|
|
||||||
<td>{{plan.interval}}</td>
|
|
||||||
<td>{{plan.unit_amount}}</td>
|
|
||||||
<td class="td-right">
|
|
||||||
<DButton
|
|
||||||
@action={{action "editPlan" plan.id}}
|
|
||||||
@icon="far-pen-to-square"
|
|
||||||
class="btn no-text btn-icon"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
@action={{route-action "destroyPlan"}}
|
|
||||||
@actionParam={{plan}}
|
|
||||||
@icon="trash-can"
|
|
||||||
class="btn-danger btn no-text btn-icon"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import icon from "discourse/helpers/d-icon";
|
||||||
|
import routeAction from "discourse/helpers/route-action";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatUnixDate from "../../helpers/format-unix-date";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{#if @controller.model.unconfigured}}
|
||||||
|
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
||||||
|
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="btn-right">
|
||||||
|
<LinkTo
|
||||||
|
@route="adminPlugins.discourse-subscriptions.products.show"
|
||||||
|
@model="new"
|
||||||
|
class="btn btn-primary"
|
||||||
|
>
|
||||||
|
{{icon "plus"}}
|
||||||
|
<span>
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.operations.new"}}
|
||||||
|
</span>
|
||||||
|
</LinkTo>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{#if @controller.model}}
|
||||||
|
<table class="table discourse-patrons-table">
|
||||||
|
<thead>
|
||||||
|
<th>
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.name"}}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.created_at"
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.updated_at"
|
||||||
|
}}
|
||||||
|
</th>
|
||||||
|
<th class="td-right">
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.active"}}
|
||||||
|
</th>
|
||||||
|
<th></th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model as |product|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{product.name}}</td>
|
||||||
|
<td>{{formatUnixDate product.created}}</td>
|
||||||
|
<td>{{formatUnixDate product.updated}}</td>
|
||||||
|
<td class="td-right">{{product.active}}</td>
|
||||||
|
<td class="td-right">
|
||||||
|
<div class="align-buttons">
|
||||||
|
<LinkTo
|
||||||
|
@route="adminPlugins.discourse-subscriptions.products.show"
|
||||||
|
@model={{product.id}}
|
||||||
|
class="btn no-text btn-icon"
|
||||||
|
>
|
||||||
|
{{icon "far-pen-to-square"}}
|
||||||
|
</LinkTo>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@action={{routeAction "destroyProduct"}}
|
||||||
|
@actionParam={{product}}
|
||||||
|
@icon="trash-can"
|
||||||
|
class="btn-danger btn no-text btn-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
<p>
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product_help"}}
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,74 +0,0 @@
|
||||||
{{#if this.model.unconfigured}}
|
|
||||||
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
|
||||||
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<p class="btn-right">
|
|
||||||
<LinkTo
|
|
||||||
@route="adminPlugins.discourse-subscriptions.products.show"
|
|
||||||
@model="new"
|
|
||||||
class="btn btn-primary"
|
|
||||||
>
|
|
||||||
{{d-icon "plus"}}
|
|
||||||
<span>
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.operations.new"}}
|
|
||||||
</span>
|
|
||||||
</LinkTo>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#if this.model}}
|
|
||||||
<table class="table discourse-patrons-table">
|
|
||||||
<thead>
|
|
||||||
<th>
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.name"}}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.created_at"}}
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.updated_at"}}
|
|
||||||
</th>
|
|
||||||
<th class="td-right">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.active"}}
|
|
||||||
</th>
|
|
||||||
<th></th>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model as |product|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{product.name}}</td>
|
|
||||||
<td>{{format-unix-date product.created}}</td>
|
|
||||||
<td>{{format-unix-date product.updated}}</td>
|
|
||||||
<td class="td-right">{{product.active}}</td>
|
|
||||||
<td class="td-right">
|
|
||||||
<div class="align-buttons">
|
|
||||||
<LinkTo
|
|
||||||
@route="adminPlugins.discourse-subscriptions.products.show"
|
|
||||||
@model={{product.id}}
|
|
||||||
class="btn no-text btn-icon"
|
|
||||||
>
|
|
||||||
{{d-icon "far-pen-to-square"}}
|
|
||||||
</LinkTo>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@action={{route-action "destroyProduct"}}
|
|
||||||
@actionParam={{product}}
|
|
||||||
@icon="trash-can"
|
|
||||||
class="btn-danger btn no-text btn-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{else}}
|
|
||||||
<p>
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product_help"}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
import { Input } from "@ember/component";
|
||||||
|
import { fn } from "@ember/helper";
|
||||||
|
import { on } from "@ember/modifier";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import ComboBox from "select-kit/components/combo-box";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<h4>{{i18n "discourse_subscriptions.admin.plans.title"}}</h4>
|
||||||
|
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<p>
|
||||||
|
<label for="product">
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.name"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="product_name"
|
||||||
|
@value={{@controller.model.product.name}}
|
||||||
|
disabled={{true}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="name">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.nickname"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="name"
|
||||||
|
@value={{@controller.model.plan.nickname}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.nickname_help"}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="interval">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.group"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<ComboBox
|
||||||
|
@valueProperty="name"
|
||||||
|
@content={{@controller.availableGroups}}
|
||||||
|
@value={{@controller.selectedGroup}}
|
||||||
|
@onChange={{fn (mut @controller.model.plan.metadata.group_name)}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.group_help"}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="amount">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.amount"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{{#if @controller.planFieldDisabled}}
|
||||||
|
<Input
|
||||||
|
class="plan-amount plan-currency"
|
||||||
|
disabled={{true}}
|
||||||
|
@value={{@controller.model.plan.currency}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<ComboBox
|
||||||
|
@disabled={{@controller.planFieldDisabled}}
|
||||||
|
@content={{@controller.currencies}}
|
||||||
|
@value={{@controller.model.plan.currency}}
|
||||||
|
@onChange={{fn (mut @controller.model.plan.currency)}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<Input
|
||||||
|
class="plan-amount"
|
||||||
|
@type="text"
|
||||||
|
name="name"
|
||||||
|
@value={{@controller.model.plan.amountDollars}}
|
||||||
|
disabled={{@controller.planFieldDisabled}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="recurring">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.recurring"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{{#if @controller.planFieldDisabled}}
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
name="recurring"
|
||||||
|
@checked={{@controller.model.plan.isRecurring}}
|
||||||
|
disabled={{true}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
name="recurring"
|
||||||
|
@checked={{@controller.model.plan.isRecurring}}
|
||||||
|
{{on "change" @controller.changeRecurring}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{#if @controller.model.plan.isRecurring}}
|
||||||
|
<p>
|
||||||
|
<label for="interval">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.interval"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
{{#if @controller.planFieldDisabled}}
|
||||||
|
<Input disabled={{true}} @value={{@controller.selectedInterval}} />
|
||||||
|
{{else}}
|
||||||
|
<ComboBox
|
||||||
|
@valueProperty="name"
|
||||||
|
@content={{@controller.availableIntervals}}
|
||||||
|
@value={{@controller.selectedInterval}}
|
||||||
|
@onChange={{fn (mut @controller.selectedInterval)}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="trial">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.trial"}}
|
||||||
|
({{i18n "discourse_subscriptions.optional"}})
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="trial"
|
||||||
|
@value={{@controller.model.plan.trial_period_days}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.trial_help"}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="active">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.active"}}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
name="active"
|
||||||
|
@checked={{@controller.model.plan.active}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<p class="control-instructions">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.operations.create_help"}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="pull-right">
|
||||||
|
{{#if @controller.model.plan.isNew}}
|
||||||
|
<DButton
|
||||||
|
@label="discourse_subscriptions.admin.plans.operations.create"
|
||||||
|
@action={{@controller.createPlan}}
|
||||||
|
@icon="plus"
|
||||||
|
class="btn btn-primary"
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
@label="discourse_subscriptions.admin.plans.operations.update"
|
||||||
|
@action={{@controller.updatePlan}}
|
||||||
|
@icon="check"
|
||||||
|
class="btn btn-primary"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,165 +0,0 @@
|
||||||
<h4>{{i18n "discourse_subscriptions.admin.plans.title"}}</h4>
|
|
||||||
|
|
||||||
<form class="form-horizontal">
|
|
||||||
<p>
|
|
||||||
<label for="product">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.name"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="product_name"
|
|
||||||
@value={{this.model.product.name}}
|
|
||||||
disabled={{true}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="name">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.nickname"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Input @type="text" name="name" @value={{this.model.plan.nickname}} />
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.nickname_help"}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="interval">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.group"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<ComboBox
|
|
||||||
@valueProperty="name"
|
|
||||||
@content={{this.availableGroups}}
|
|
||||||
@value={{this.selectedGroup}}
|
|
||||||
@onChange={{action (mut this.model.plan.metadata.group_name)}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.group_help"}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="amount">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.amount"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{{#if this.planFieldDisabled}}
|
|
||||||
<Input
|
|
||||||
class="plan-amount plan-currency"
|
|
||||||
disabled={{true}}
|
|
||||||
@value={{this.model.plan.currency}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<ComboBox
|
|
||||||
@disabled={{this.planFieldDisabled}}
|
|
||||||
@content={{this.currencies}}
|
|
||||||
@value={{this.model.plan.currency}}
|
|
||||||
@onChange={{action (mut this.model.plan.currency)}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
class="plan-amount"
|
|
||||||
@type="text"
|
|
||||||
name="name"
|
|
||||||
@value={{this.model.plan.amountDollars}}
|
|
||||||
disabled={{this.planFieldDisabled}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="recurring">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.recurring"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{{#if this.planFieldDisabled}}
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
name="recurring"
|
|
||||||
@checked={{this.model.plan.isRecurring}}
|
|
||||||
disabled={{true}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
name="recurring"
|
|
||||||
@checked={{this.model.plan.isRecurring}}
|
|
||||||
{{on "change" (action "changeRecurring")}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{#if this.model.plan.isRecurring}}
|
|
||||||
<p>
|
|
||||||
<label for="interval">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.interval"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
{{#if this.planFieldDisabled}}
|
|
||||||
<Input disabled={{true}} @value={{this.selectedInterval}} />
|
|
||||||
{{else}}
|
|
||||||
<ComboBox
|
|
||||||
@valueProperty="name"
|
|
||||||
@content={{this.availableIntervals}}
|
|
||||||
@value={{this.selectedInterval}}
|
|
||||||
@onChange={{action (mut this.selectedInterval)}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="trial">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.trial"}}
|
|
||||||
({{i18n "discourse_subscriptions.optional"}})
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="trial"
|
|
||||||
@value={{this.model.plan.trial_period_days}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.trial_help"}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="active">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.active"}}
|
|
||||||
</label>
|
|
||||||
<Input @type="checkbox" name="active" @checked={{this.model.plan.active}} />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<p class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.operations.create_help"}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="pull-right">
|
|
||||||
{{#if this.model.plan.isNew}}
|
|
||||||
<DButton
|
|
||||||
@label="discourse_subscriptions.admin.plans.operations.create"
|
|
||||||
@action={{action "createPlan"}}
|
|
||||||
@icon="plus"
|
|
||||||
class="btn btn-primary"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
@label="discourse_subscriptions.admin.plans.operations.update"
|
|
||||||
@action={{action "updatePlan"}}
|
|
||||||
@icon="check"
|
|
||||||
class="btn btn-primary"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
import { Input, Textarea } from "@ember/component";
|
||||||
|
import { array } from "@ember/helper";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import icon from "discourse/helpers/d-icon";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatCurrency from "../../helpers/format-currency";
|
||||||
|
import formatUnixDate from "../../helpers/format-unix-date";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<h4>{{i18n "discourse_subscriptions.admin.products.title"}}</h4>
|
||||||
|
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<p>
|
||||||
|
<label for="name">
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.name"}}
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="name"
|
||||||
|
@value={{@controller.model.product.name}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="description">
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.description"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Textarea
|
||||||
|
name="description"
|
||||||
|
@value={{@controller.model.product.metadata.description}}
|
||||||
|
class="discourse-subscriptions-admin-textarea"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.description_help"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="statement_descriptor">
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.statement_descriptor"
|
||||||
|
}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="statement_descriptor"
|
||||||
|
@value={{@controller.model.product.statement_descriptor}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.statement_descriptor_help"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="repurchaseable">
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.repurchaseable"
|
||||||
|
}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
name="repurchaseable"
|
||||||
|
@checked={{@controller.model.product.metadata.repurchaseable}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.repurchase_help"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<label for="active">
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.active"}}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
name="active"
|
||||||
|
@checked={{@controller.model.product.active}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="control-instructions">
|
||||||
|
{{i18n "discourse_subscriptions.admin.products.product.active_help"}}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{#unless @controller.model.product.isNew}}
|
||||||
|
<h4>{{i18n "discourse_subscriptions.admin.plans.title"}}</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<table class="table discourse-patrons-table">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.admin.plans.plan.nickname"
|
||||||
|
}}</th>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.admin.plans.plan.interval"
|
||||||
|
}}</th>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.admin.plans.plan.created_at"
|
||||||
|
}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.plans.plan.group"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.admin.plans.plan.active"}}</th>
|
||||||
|
<th class="td-right">
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.plan.amount"}}
|
||||||
|
</th>
|
||||||
|
<th class="td-right">
|
||||||
|
<LinkTo
|
||||||
|
@route="adminPlugins.discourse-subscriptions.products.show.plans.show"
|
||||||
|
@models={{array @controller.model.product.id "new"}}
|
||||||
|
class="btn"
|
||||||
|
>
|
||||||
|
{{i18n "discourse_subscriptions.admin.plans.operations.add"}}
|
||||||
|
</LinkTo>
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model.plans as |plan|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{plan.nickname}}</td>
|
||||||
|
<td>{{plan.recurring.interval}}</td>
|
||||||
|
<td>{{formatUnixDate plan.created}}</td>
|
||||||
|
<td>{{plan.metadata.group_name}}</td>
|
||||||
|
<td>{{plan.active}}</td>
|
||||||
|
<td class="td-right">
|
||||||
|
{{formatCurrency plan.currency plan.amountDollars}}
|
||||||
|
</td>
|
||||||
|
<td class="td-right">
|
||||||
|
<LinkTo
|
||||||
|
@route="adminPlugins.discourse-subscriptions.products.show.plans.show"
|
||||||
|
@models={{array @controller.model.product.id plan.id}}
|
||||||
|
class="btn no-text btn-icon"
|
||||||
|
>
|
||||||
|
{{icon "far-pen-to-square"}}
|
||||||
|
</LinkTo>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<tr>
|
||||||
|
<td colspan="8">
|
||||||
|
<hr />
|
||||||
|
{{i18n
|
||||||
|
"discourse_subscriptions.admin.products.product.plan_help"
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
<div class="pull-right">
|
||||||
|
<DButton
|
||||||
|
@label="cancel"
|
||||||
|
@action={{@controller.cancelProduct}}
|
||||||
|
@icon="xmark"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{#if @controller.model.product.isNew}}
|
||||||
|
<DButton
|
||||||
|
@label="discourse_subscriptions.admin.products.operations.create"
|
||||||
|
@action={{@controller.createProduct}}
|
||||||
|
@icon="plus"
|
||||||
|
class="btn btn-primary"
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
@label="discourse_subscriptions.admin.products.operations.update"
|
||||||
|
@action={{@controller.updateProduct}}
|
||||||
|
@icon="check"
|
||||||
|
class="btn btn-primary"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{outlet}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,161 +0,0 @@
|
||||||
<h4>{{i18n "discourse_subscriptions.admin.products.title"}}</h4>
|
|
||||||
|
|
||||||
<form class="form-horizontal">
|
|
||||||
<p>
|
|
||||||
<label for="name">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.name"}}
|
|
||||||
</label>
|
|
||||||
<Input @type="text" name="name" @value={{this.model.product.name}} />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="description">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.description"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Textarea
|
|
||||||
name="description"
|
|
||||||
@value={{this.model.product.metadata.description}}
|
|
||||||
class="discourse-subscriptions-admin-textarea"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.description_help"}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="statement_descriptor">
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.admin.products.product.statement_descriptor"
|
|
||||||
}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="statement_descriptor"
|
|
||||||
@value={{this.model.product.statement_descriptor}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.admin.products.product.statement_descriptor_help"
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="repurchaseable">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.repurchaseable"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
name="repurchaseable"
|
|
||||||
@checked={{this.model.product.metadata.repurchaseable}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.repurchase_help"}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<label for="active">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.active"}}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
@type="checkbox"
|
|
||||||
name="active"
|
|
||||||
@checked={{this.model.product.active}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="control-instructions">
|
|
||||||
{{i18n "discourse_subscriptions.admin.products.product.active_help"}}
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{{#unless this.model.product.isNew}}
|
|
||||||
<h4>{{i18n "discourse_subscriptions.admin.plans.title"}}</h4>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<table class="table discourse-patrons-table">
|
|
||||||
<thead>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.nickname"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.interval"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.created_at"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.group"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.admin.plans.plan.active"}}</th>
|
|
||||||
<th class="td-right">
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.plan.amount"}}
|
|
||||||
</th>
|
|
||||||
<th class="td-right">
|
|
||||||
<LinkTo
|
|
||||||
@route="adminPlugins.discourse-subscriptions.products.show.plans.show"
|
|
||||||
@models={{array this.model.product.id "new"}}
|
|
||||||
class="btn"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.admin.plans.operations.add"}}
|
|
||||||
</LinkTo>
|
|
||||||
</th>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model.plans as |plan|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{plan.nickname}}</td>
|
|
||||||
<td>{{plan.recurring.interval}}</td>
|
|
||||||
<td>{{format-unix-date plan.created}}</td>
|
|
||||||
<td>{{plan.metadata.group_name}}</td>
|
|
||||||
<td>{{plan.active}}</td>
|
|
||||||
<td class="td-right">
|
|
||||||
{{format-currency plan.currency plan.amountDollars}}
|
|
||||||
</td>
|
|
||||||
<td class="td-right">
|
|
||||||
<LinkTo
|
|
||||||
@route="adminPlugins.discourse-subscriptions.products.show.plans.show"
|
|
||||||
@models={{array this.model.product.id plan.id}}
|
|
||||||
class="btn no-text btn-icon"
|
|
||||||
>
|
|
||||||
{{d-icon "far-pen-to-square"}}
|
|
||||||
</LinkTo>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{else}}
|
|
||||||
<tr>
|
|
||||||
<td colspan="8">
|
|
||||||
<hr />
|
|
||||||
{{i18n
|
|
||||||
"discourse_subscriptions.admin.products.product.plan_help"
|
|
||||||
}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</p>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
<div class="pull-right">
|
|
||||||
<DButton @label="cancel" @action={{action "cancelProduct"}} @icon="xmark" />
|
|
||||||
|
|
||||||
{{#if this.model.product.isNew}}
|
|
||||||
<DButton
|
|
||||||
@label="discourse_subscriptions.admin.products.operations.create"
|
|
||||||
@action={{action "createProduct"}}
|
|
||||||
@icon="plus"
|
|
||||||
class="btn btn-primary"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
@label="discourse_subscriptions.admin.products.operations.update"
|
|
||||||
@action={{action "updateProduct"}}
|
|
||||||
@icon="check"
|
|
||||||
class="btn btn-primary"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{outlet}}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
|
||||||
|
export default RouteTemplate(<template>{{outlet}}</template>);
|
|
@ -1 +0,0 @@
|
||||||
{{outlet}}
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { fn } from "@ember/helper";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import LoadMore from "discourse/components/load-more";
|
||||||
|
import loadingSpinner from "discourse/helpers/loading-spinner";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatUnixDate from "../../helpers/format-unix-date";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{#if @controller.model.unconfigured}}
|
||||||
|
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
||||||
|
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<LoadMore
|
||||||
|
@selector=".discourse-patrons-table tr"
|
||||||
|
@action={{@controller.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>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model.data as |subscription|}}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{#if subscription.metadataUserExists}}
|
||||||
|
<a href={{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">
|
||||||
|
{{formatUnixDate subscription.created}}
|
||||||
|
</td>
|
||||||
|
<td class="td-right">
|
||||||
|
{{#if subscription.loading}}
|
||||||
|
{{loadingSpinner size="small"}}
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
@disabled={{subscription.canceled}}
|
||||||
|
@label="cancel"
|
||||||
|
@action={{fn @controller.showCancelModal subscription}}
|
||||||
|
@icon="xmark"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</LoadMore>
|
||||||
|
|
||||||
|
<ConditionalLoadingSpinner @condition={{@controller.loading}} />
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,90 +0,0 @@
|
||||||
{{#if this.model.unconfigured}}
|
|
||||||
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
|
||||||
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<LoadMore
|
|
||||||
@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>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model.data as |subscription|}}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
{{#if subscription.metadataUserExists}}
|
|
||||||
<a href={{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}}
|
|
||||||
<DButton
|
|
||||||
@disabled={{subscription.canceled}}
|
|
||||||
@label="cancel"
|
|
||||||
@action={{action "showCancelModal" subscription}}
|
|
||||||
@icon="xmark"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</LoadMore>
|
|
||||||
|
|
||||||
<ConditionalLoadingSpinner @condition={{this.loading}} />
|
|
||||||
{{/if}}
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import NavItem from "discourse/components/nav-item";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<h2>{{i18n
|
||||||
|
"discourse_subscriptions.title"
|
||||||
|
site_name=@controller.siteSettings.title
|
||||||
|
}}</h2>
|
||||||
|
|
||||||
|
{{#if @controller.stripeConfigured}}
|
||||||
|
<div class="discourse-subscriptions-buttons">
|
||||||
|
{{#if @controller.campaignEnabled}}
|
||||||
|
<DButton
|
||||||
|
@label="discourse_subscriptions.campaign.refresh_campaign"
|
||||||
|
@icon="rotate"
|
||||||
|
@action={{@controller.triggerManualRefresh}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
{{#unless @controller.campaignProductSet}}
|
||||||
|
<DButton
|
||||||
|
@label="discourse_subscriptions.campaign.one_click_campaign"
|
||||||
|
@icon="square-plus"
|
||||||
|
@action={{@controller.createOneClickCampaign}}
|
||||||
|
@isLoading={{@controller.loading}}
|
||||||
|
/>
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="nav nav-pills">
|
||||||
|
<NavItem
|
||||||
|
@route="adminPlugins.discourse-subscriptions.products"
|
||||||
|
@label="discourse_subscriptions.admin.products.title"
|
||||||
|
/>
|
||||||
|
<NavItem
|
||||||
|
@route="adminPlugins.discourse-subscriptions.coupons"
|
||||||
|
@label="discourse_subscriptions.admin.coupons.title"
|
||||||
|
/>
|
||||||
|
<NavItem
|
||||||
|
@route="adminPlugins.discourse-subscriptions.subscriptions"
|
||||||
|
@label="discourse_subscriptions.admin.subscriptions.title"
|
||||||
|
/>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div id="discourse-subscriptions-admin">
|
||||||
|
{{outlet}}
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
||||||
|
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,53 +0,0 @@
|
||||||
<h2>{{i18n
|
|
||||||
"discourse_subscriptions.title"
|
|
||||||
site_name=this.siteSettings.title
|
|
||||||
}}</h2>
|
|
||||||
|
|
||||||
{{#if this.stripeConfigured}}
|
|
||||||
<div class="discourse-subscriptions-buttons">
|
|
||||||
{{#if this.campaignEnabled}}
|
|
||||||
<DButton
|
|
||||||
@label="discourse_subscriptions.campaign.refresh_campaign"
|
|
||||||
@icon="rotate"
|
|
||||||
@action={{action "triggerManualRefresh"}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
{{#unless this.campaignProductSet}}
|
|
||||||
<DButton
|
|
||||||
@label="discourse_subscriptions.campaign.one_click_campaign"
|
|
||||||
@icon="square-plus"
|
|
||||||
@action={{action "createOneClickCampaign"}}
|
|
||||||
@isLoading={{this.loading}}
|
|
||||||
/>
|
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class="nav nav-pills">
|
|
||||||
<NavItem
|
|
||||||
@route="adminPlugins.discourse-subscriptions.products"
|
|
||||||
@label="discourse_subscriptions.admin.products.title"
|
|
||||||
/>
|
|
||||||
<NavItem
|
|
||||||
@route="adminPlugins.discourse-subscriptions.coupons"
|
|
||||||
@label="discourse_subscriptions.admin.coupons.title"
|
|
||||||
/>
|
|
||||||
<NavItem
|
|
||||||
@route="adminPlugins.discourse-subscriptions.subscriptions"
|
|
||||||
@label="discourse_subscriptions.admin.subscriptions.title"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<div id="discourse-subscriptions-admin">
|
|
||||||
{{outlet}}
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<p>{{i18n "discourse_subscriptions.admin.unconfigured"}}</p>
|
|
||||||
<p>
|
|
||||||
<a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">
|
|
||||||
{{i18n "discourse_subscriptions.admin.on_meta"}}
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="title-wrapper">
|
||||||
|
<h1>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.title"}}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{{outlet}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,11 +0,0 @@
|
||||||
<div class="container">
|
|
||||||
<div class="title-wrapper">
|
|
||||||
<h1>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.title"}}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
{{outlet}}
|
|
||||||
</div>
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import LoginRequired from "../../components/login-required";
|
||||||
|
import ProductList from "../../components/product-list";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{#unless @controller.isLoggedIn}}
|
||||||
|
<LoginRequired />
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
<ProductList
|
||||||
|
@products={{@controller.model}}
|
||||||
|
@isLoggedIn={{@controller.isLoggedIn}}
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,5 +0,0 @@
|
||||||
{{#unless this.isLoggedIn}}
|
|
||||||
<LoginRequired />
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
<ProductList @products={{this.model}} @isLoggedIn={{this.isLoggedIn}} />
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
import { Input } from "@ember/component";
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import htmlSafe from "discourse/helpers/html-safe";
|
||||||
|
import loadingSpinner from "discourse/helpers/loading-spinner";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import LoginRequired from "../../components/login-required";
|
||||||
|
import PaymentOptions from "../../components/payment-options";
|
||||||
|
import SubscribeCaProvinceSelect from "../../components/subscribe-ca-province-select";
|
||||||
|
import SubscribeCard from "../../components/subscribe-card";
|
||||||
|
import SubscribeCountrySelect from "../../components/subscribe-country-select";
|
||||||
|
import SubscribeUsStateSelect from "../../components/subscribe-us-state-select";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<div class="discourse-subscriptions-section-columns">
|
||||||
|
<div class="section-column discourse-subscriptions-confirmation-billing">
|
||||||
|
<h2>
|
||||||
|
{{@controller.model.product.name}}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{htmlSafe @controller.model.product.description}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-column">
|
||||||
|
{{#if @controller.canPurchase}}
|
||||||
|
<h2>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.card.title"}}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<PaymentOptions
|
||||||
|
@plans={{@controller.model.plans}}
|
||||||
|
@selectedPlan={{@controller.selectedPlan}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<SubscribeCard @cardElement={{@controller.cardElement}} />
|
||||||
|
|
||||||
|
{{#if @controller.loading}}
|
||||||
|
{{loadingSpinner}}
|
||||||
|
{{else if @controller.isAnonymous}}
|
||||||
|
<LoginRequired />
|
||||||
|
{{else}}
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="cardholder_name"
|
||||||
|
placeholder={{i18n
|
||||||
|
"discourse_subscriptions.subscribe.cardholder_name"
|
||||||
|
}}
|
||||||
|
@value={{@controller.cardholderName}}
|
||||||
|
class="subscribe-name"
|
||||||
|
/>
|
||||||
|
<div class="address-fields">
|
||||||
|
<SubscribeCountrySelect
|
||||||
|
@value={{@controller.cardholderAddress.country}}
|
||||||
|
@onChange={{@controller.changeCountry}}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="cardholder_postal_code"
|
||||||
|
placeholder={{i18n
|
||||||
|
"discourse_subscriptions.subscribe.cardholder_address.postal_code"
|
||||||
|
}}
|
||||||
|
@value={{@controller.cardholderAddress.postalCode}}
|
||||||
|
class="subscribe-address-postal-code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="cardholder_line1"
|
||||||
|
placeholder={{i18n
|
||||||
|
"discourse_subscriptions.subscribe.cardholder_address.line1"
|
||||||
|
}}
|
||||||
|
@value={{@controller.cardholderAddress.line1}}
|
||||||
|
class="subscribe-address-line1"
|
||||||
|
/>
|
||||||
|
<div class="address-fields">
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="cardholder_city"
|
||||||
|
placeholder={{i18n
|
||||||
|
"discourse_subscriptions.subscribe.cardholder_address.city"
|
||||||
|
}}
|
||||||
|
@value={{@controller.cardholderAddress.city}}
|
||||||
|
class="subscribe-address-city"
|
||||||
|
/>
|
||||||
|
{{#if @controller.isCountryUS}}
|
||||||
|
<SubscribeUsStateSelect
|
||||||
|
@value={{@controller.cardholderAddress.state}}
|
||||||
|
@onChange={{@controller.changeState}}
|
||||||
|
/>
|
||||||
|
{{else if @controller.isCountryCA}}
|
||||||
|
<SubscribeCaProvinceSelect
|
||||||
|
@value={{@controller.cardholderAddress.state}}
|
||||||
|
@onChange={{@controller.changeState}}
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="cardholder_state"
|
||||||
|
placeholder={{i18n
|
||||||
|
"discourse_subscriptions.subscribe.cardholder_address.state"
|
||||||
|
}}
|
||||||
|
@value={{@controller.cardholderAddress.state}}
|
||||||
|
class="subscribe-address-state"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
@type="text"
|
||||||
|
name="promo_code"
|
||||||
|
placeholder={{i18n
|
||||||
|
"discourse_subscriptions.subscribe.promo_code"
|
||||||
|
}}
|
||||||
|
@value={{@controller.promoCode}}
|
||||||
|
class="subscribe-promo-code"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@disabled={{@controller.loading}}
|
||||||
|
@action={{@controller.stripePaymentHandler}}
|
||||||
|
class="btn btn-primary btn-payment"
|
||||||
|
@label="discourse_subscriptions.plans.payment_button"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<h2>{{i18n
|
||||||
|
"discourse_subscriptions.subscribe.already_purchased"
|
||||||
|
}}</h2>
|
||||||
|
|
||||||
|
<LinkTo
|
||||||
|
@route="user.billing.subscriptions"
|
||||||
|
@model={{@controller.currentUser.username}}
|
||||||
|
class="btn btn-primary"
|
||||||
|
>
|
||||||
|
{{i18n "discourse_subscriptions.subscribe.go_to_billing"}}
|
||||||
|
</LinkTo>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,129 +0,0 @@
|
||||||
<div class="discourse-subscriptions-section-columns">
|
|
||||||
<div class="section-column discourse-subscriptions-confirmation-billing">
|
|
||||||
<h2>
|
|
||||||
{{this.model.product.name}}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{{html-safe this.model.product.description}}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section-column">
|
|
||||||
{{#if this.canPurchase}}
|
|
||||||
<h2>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.card.title"}}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<PaymentOptions
|
|
||||||
@plans={{this.model.plans}}
|
|
||||||
@selectedPlan={{this.selectedPlan}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<SubscribeCard @cardElement={{this.cardElement}} />
|
|
||||||
|
|
||||||
{{#if this.loading}}
|
|
||||||
{{loading-spinner}}
|
|
||||||
{{else if this.isAnonymous}}
|
|
||||||
<LoginRequired />
|
|
||||||
{{else}}
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="cardholder_name"
|
|
||||||
placeholder={{i18n
|
|
||||||
"discourse_subscriptions.subscribe.cardholder_name"
|
|
||||||
}}
|
|
||||||
@value={{this.cardholderName}}
|
|
||||||
class="subscribe-name"
|
|
||||||
/>
|
|
||||||
<div class="address-fields">
|
|
||||||
<SubscribeCountrySelect
|
|
||||||
@value={{this.cardholderAddress.country}}
|
|
||||||
@onChange={{action "changeCountry"}}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="cardholder_postal_code"
|
|
||||||
placeholder={{i18n
|
|
||||||
"discourse_subscriptions.subscribe.cardholder_address.postal_code"
|
|
||||||
}}
|
|
||||||
@value={{this.cardholderAddress.postalCode}}
|
|
||||||
class="subscribe-address-postal-code"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="cardholder_line1"
|
|
||||||
placeholder={{i18n
|
|
||||||
"discourse_subscriptions.subscribe.cardholder_address.line1"
|
|
||||||
}}
|
|
||||||
@value={{this.cardholderAddress.line1}}
|
|
||||||
class="subscribe-address-line1"
|
|
||||||
/>
|
|
||||||
<div class="address-fields">
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="cardholder_city"
|
|
||||||
placeholder={{i18n
|
|
||||||
"discourse_subscriptions.subscribe.cardholder_address.city"
|
|
||||||
}}
|
|
||||||
@value={{this.cardholderAddress.city}}
|
|
||||||
class="subscribe-address-city"
|
|
||||||
/>
|
|
||||||
{{#if this.isCountryUS}}
|
|
||||||
<SubscribeUsStateSelect
|
|
||||||
@value={{this.cardholderAddress.state}}
|
|
||||||
@onChange={{action "changeState"}}
|
|
||||||
/>
|
|
||||||
{{else if this.isCountryCA}}
|
|
||||||
<SubscribeCaProvinceSelect
|
|
||||||
@value={{this.cardholderAddress.state}}
|
|
||||||
@onChange={{action "changeState"}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="cardholder_state"
|
|
||||||
placeholder={{i18n
|
|
||||||
"discourse_subscriptions.subscribe.cardholder_address.state"
|
|
||||||
}}
|
|
||||||
@value={{this.cardholderAddress.state}}
|
|
||||||
class="subscribe-address-state"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
@type="text"
|
|
||||||
name="promo_code"
|
|
||||||
placeholder={{i18n "discourse_subscriptions.subscribe.promo_code"}}
|
|
||||||
@value={{this.promoCode}}
|
|
||||||
class="subscribe-promo-code"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@disabled={{this.loading}}
|
|
||||||
@action={{action "stripePaymentHandler"}}
|
|
||||||
class="btn btn-primary btn-payment"
|
|
||||||
@label="discourse_subscriptions.plans.payment_button"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<h2>{{i18n "discourse_subscriptions.subscribe.already_purchased"}}</h2>
|
|
||||||
|
|
||||||
<LinkTo
|
|
||||||
@route="user.billing.subscriptions"
|
|
||||||
@model={{this.currentUser.username}}
|
|
||||||
class="btn btn-primary"
|
|
||||||
>
|
|
||||||
{{i18n "discourse_subscriptions.subscribe.go_to_billing"}}
|
|
||||||
</LinkTo>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import LoginRequired from "../components/login-required";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
{{#if @controller.currentUser}}
|
||||||
|
{{@controller.pricingTable}}
|
||||||
|
{{else}}
|
||||||
|
<LoginRequired />
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,7 +0,0 @@
|
||||||
<div class="container">
|
|
||||||
{{#if this.currentUser}}
|
|
||||||
{{this.pricingTable}}
|
|
||||||
{{else}}
|
|
||||||
<LoginRequired />
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { LinkTo } from "@ember/routing";
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import MobileNav from "discourse/components/mobile-nav";
|
||||||
|
import bodyClass from "discourse/helpers/body-class";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{bodyClass "user-billing-page"}}
|
||||||
|
|
||||||
|
<section class="user-secondary-navigation">
|
||||||
|
<MobileNav
|
||||||
|
@desktopClass="action-list nav-stacked"
|
||||||
|
@currentPath={{@controller.router._router.currentPath}}
|
||||||
|
class="activity-nav"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<LinkTo @route="user.billing.subscriptions">
|
||||||
|
{{i18n "discourse_subscriptions.navigation.subscriptions"}}
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<LinkTo @route="user.billing.payments">
|
||||||
|
{{i18n "discourse_subscriptions.navigation.payments"}}
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
</MobileNav>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="user-content">
|
||||||
|
{{outlet}}
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,25 +0,0 @@
|
||||||
{{body-class "user-billing-page"}}
|
|
||||||
|
|
||||||
<section class="user-secondary-navigation">
|
|
||||||
<MobileNav
|
|
||||||
@desktopClass="action-list nav-stacked"
|
|
||||||
@currentPath={{this.router._router.currentPath}}
|
|
||||||
class="activity-nav"
|
|
||||||
>
|
|
||||||
<li>
|
|
||||||
<LinkTo @route="user.billing.subscriptions">
|
|
||||||
{{i18n "discourse_subscriptions.navigation.subscriptions"}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<LinkTo @route="user.billing.payments">
|
|
||||||
{{i18n "discourse_subscriptions.navigation.payments"}}
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
</MobileNav>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="user-content">
|
|
||||||
{{outlet}}
|
|
||||||
</section>
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
|
||||||
|
export default RouteTemplate(<template>BILLING INDEX</template>);
|
|
@ -1 +0,0 @@
|
||||||
BILLING INDEX
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatCurrency from "../../../helpers/format-currency";
|
||||||
|
import formatUnixDate from "../../../helpers/format-unix-date";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{#if @controller.model}}
|
||||||
|
<table class="table discourse-subscriptions-user-table">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.payments.id"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.payments.amount"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.payments.created_at"}}</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model as |payment|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{payment.id}}</td>
|
||||||
|
<td>{{formatCurrency payment.currency payment.amountDollars}}</td>
|
||||||
|
<td>{{formatUnixDate payment.created}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
{{i18n "discourse_subscriptions.user.payments_help"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,22 +0,0 @@
|
||||||
{{#if this.model}}
|
|
||||||
<table class="table discourse-subscriptions-user-table">
|
|
||||||
<thead>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.payments.id"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.payments.amount"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.payments.created_at"}}</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model as |payment|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{payment.id}}</td>
|
|
||||||
<td>{{format-currency payment.currency payment.amountDollars}}</td>
|
|
||||||
<td>{{format-unix-date payment.created}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{else}}
|
|
||||||
<div class="alert alert-info">
|
|
||||||
{{i18n "discourse_subscriptions.user.payments_help"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import SaveControls from "discourse/components/save-controls";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import SubscribeCard from "../../../../components/subscribe-card";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
<h3>{{i18n
|
||||||
|
"discourse_subscriptions.user.subscriptions.update_card.heading"
|
||||||
|
sub_id=@controller.model
|
||||||
|
}}</h3>
|
||||||
|
|
||||||
|
<div class="form-vertical">
|
||||||
|
<div class="control-group">
|
||||||
|
<SubscribeCard
|
||||||
|
@cardElement={{@controller.cardElement}}
|
||||||
|
class="input-xxlarge"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SaveControls
|
||||||
|
@action={{@controller.updatePaymentMethod}}
|
||||||
|
@saved={{@controller.saved}}
|
||||||
|
@saveDisabled={{@controller.loading}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,16 +0,0 @@
|
||||||
<h3>{{i18n
|
|
||||||
"discourse_subscriptions.user.subscriptions.update_card.heading"
|
|
||||||
sub_id=this.model
|
|
||||||
}}</h3>
|
|
||||||
|
|
||||||
<div class="form-vertical">
|
|
||||||
<div class="control-group">
|
|
||||||
<SubscribeCard @cardElement={{this.cardElement}} class="input-xxlarge" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SaveControls
|
|
||||||
@action={{action "updatePaymentMethod"}}
|
|
||||||
@saved={{this.saved}}
|
|
||||||
@saveDisabled={{this.loading}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import RouteTemplate from "ember-route-template";
|
||||||
|
import DButton from "discourse/components/d-button";
|
||||||
|
import loadingSpinner from "discourse/helpers/loading-spinner";
|
||||||
|
import routeAction from "discourse/helpers/route-action";
|
||||||
|
import { i18n } from "discourse-i18n";
|
||||||
|
import formatUnixDate from "../../../../helpers/format-unix-date";
|
||||||
|
|
||||||
|
export default RouteTemplate(
|
||||||
|
<template>
|
||||||
|
{{#if @controller.model}}
|
||||||
|
<table class="table discourse-subscriptions-user-table">
|
||||||
|
<thead>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.subscriptions.id"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.plans.product"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.plans.rate"}}</th>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.user.subscriptions.discounted"
|
||||||
|
}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.subscriptions.status"}}</th>
|
||||||
|
<th>{{i18n "discourse_subscriptions.user.subscriptions.renews"}}</th>
|
||||||
|
<th>{{i18n
|
||||||
|
"discourse_subscriptions.user.subscriptions.created_at"
|
||||||
|
}}</th>
|
||||||
|
<th></th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each @controller.model as |subscription|}}
|
||||||
|
<tr>
|
||||||
|
<td>{{subscription.id}}</td>
|
||||||
|
<td>{{subscription.product.name}}</td>
|
||||||
|
<td>{{subscription.plan.subscriptionRate}}</td>
|
||||||
|
<td>{{subscription.discounted}}</td>
|
||||||
|
<td>{{subscription.status}}</td>
|
||||||
|
<td>{{subscription.endDate}}</td>
|
||||||
|
<td>{{formatUnixDate subscription.created}}</td>
|
||||||
|
<td class="td-right">
|
||||||
|
{{#if subscription.loading}}
|
||||||
|
{{loadingSpinner size="small"}}
|
||||||
|
{{else}}
|
||||||
|
{{#if subscription.canceled_at}}
|
||||||
|
<DButton
|
||||||
|
@disabled={{subscription.canceled_at}}
|
||||||
|
@label="discourse_subscriptions.user.subscriptions.cancelled"
|
||||||
|
/>
|
||||||
|
{{else}}
|
||||||
|
<DButton
|
||||||
|
@action={{routeAction "updateCard" subscription.id}}
|
||||||
|
@icon="far-pen-to-square"
|
||||||
|
class="btn no-text btn-icon"
|
||||||
|
/>
|
||||||
|
<DButton
|
||||||
|
class="btn-danger btn no-text btn-icon"
|
||||||
|
@icon="trash-can"
|
||||||
|
@disabled={{subscription.canceled_at}}
|
||||||
|
@action={{routeAction "cancelSubscription" subscription}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
<div class="alert alert-info">
|
||||||
|
{{i18n "discourse_subscriptions.user.subscriptions_help"}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</template>
|
||||||
|
);
|
|
@ -1,55 +0,0 @@
|
||||||
{{#if this.model}}
|
|
||||||
<table class="table discourse-subscriptions-user-table">
|
|
||||||
<thead>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.subscriptions.id"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.plans.product"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.plans.rate"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.subscriptions.discounted"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.subscriptions.status"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.subscriptions.renews"}}</th>
|
|
||||||
<th>{{i18n "discourse_subscriptions.user.subscriptions.created_at"}}</th>
|
|
||||||
<th></th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{#each this.model as |subscription|}}
|
|
||||||
<tr>
|
|
||||||
<td>{{subscription.id}}</td>
|
|
||||||
<td>{{subscription.product.name}}</td>
|
|
||||||
<td>{{subscription.plan.subscriptionRate}}</td>
|
|
||||||
<td>{{subscription.discounted}}</td>
|
|
||||||
<td>{{subscription.status}}</td>
|
|
||||||
<td>{{subscription.endDate}}</td>
|
|
||||||
<td>{{format-unix-date subscription.created}}</td>
|
|
||||||
<td class="td-right">
|
|
||||||
{{#if subscription.loading}}
|
|
||||||
{{loading-spinner size="small"}}
|
|
||||||
{{else}}
|
|
||||||
{{#if subscription.canceled_at}}
|
|
||||||
<DButton
|
|
||||||
@disabled={{subscription.canceled_at}}
|
|
||||||
@label="discourse_subscriptions.user.subscriptions.cancelled"
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<DButton
|
|
||||||
@action={{route-action "updateCard" subscription.id}}
|
|
||||||
@icon="far-pen-to-square"
|
|
||||||
class="btn no-text btn-icon"
|
|
||||||
/>
|
|
||||||
<DButton
|
|
||||||
class="btn-danger btn no-text btn-icon"
|
|
||||||
@icon="trash-can"
|
|
||||||
@disabled={{subscription.canceled_at}}
|
|
||||||
@action={{route-action "cancelSubscription" subscription}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{{else}}
|
|
||||||
<div class="alert alert-info">
|
|
||||||
{{i18n "discourse_subscriptions.user.subscriptions_help"}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
@use "lib/viewport";
|
||||||
/* stylelint-disable scss/no-global-function-names */
|
/* stylelint-disable scss/no-global-function-names */
|
||||||
.subscription-campaign-sidebar {
|
.subscription-campaign-sidebar {
|
||||||
#main-outlet
|
#main-outlet
|
||||||
|
@ -106,7 +107,7 @@ body.archetype-regular {
|
||||||
box-shadow: 5px 5px
|
box-shadow: 5px 5px
|
||||||
var(--discourse_subscriptions_campaign_banner_shadow_color);
|
var(--discourse_subscriptions_campaign_banner_shadow_color);
|
||||||
|
|
||||||
@include breakpoint(tablet) {
|
@include viewport.until(md) {
|
||||||
width: 98%;
|
width: 98%;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
@ -131,7 +132,7 @@ body.archetype-regular {
|
||||||
padding: 2em;
|
padding: 2em;
|
||||||
background-color: var(--primary-very-low);
|
background-color: var(--primary-very-low);
|
||||||
|
|
||||||
@include breakpoint(tablet) {
|
@include viewport.until(md) {
|
||||||
width: calc(100% - 4em);
|
width: calc(100% - 4em);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -143,7 +144,7 @@ body.archetype-regular {
|
||||||
font-size: $font-up-4;
|
font-size: $font-up-4;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
@include breakpoint(tablet) {
|
@include viewport.until(md) {
|
||||||
font-size: $font-up-3;
|
font-size: $font-up-3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +158,7 @@ body.archetype-regular {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0.25em 0 1em 0;
|
margin: 0.25em 0 1em 0;
|
||||||
|
|
||||||
@include breakpoint(tablet) {
|
@include viewport.until(md) {
|
||||||
font-size: $font-down-1;
|
font-size: $font-down-1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -194,7 +195,7 @@ body.archetype-regular {
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
@include breakpoint(tablet) {
|
@include viewport.until(md) {
|
||||||
width: calc(100% - 4em);
|
width: calc(100% - 4em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +292,7 @@ html:not(.mobile-view) .subscriptions-campaign-topic-footer .campaign-banner {
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
width: calc(var(--d-max-width) * 0.87);
|
width: calc(var(--d-max-width) * 0.87);
|
||||||
|
|
||||||
@include breakpoint(large) {
|
@include viewport.until(lg) {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +303,7 @@ html:not(.mobile-view) .subscriptions-campaign-topic-footer .campaign-banner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Topic Footer Version + Sidebar visible
|
// Topic Footer Version + Sidebar visible
|
||||||
@media screen and (max-width: 1285px) {
|
@media screen and (width <= 1285px) {
|
||||||
html:not(.mobile-view)
|
html:not(.mobile-view)
|
||||||
body.has-sidebar-page
|
body.has-sidebar-page
|
||||||
.subscriptions-campaign-topic-footer
|
.subscriptions-campaign-topic-footer
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
@use "lib/viewport";
|
||||||
|
|
||||||
.discourse-subscriptions-section-columns {
|
.discourse-subscriptions-section-columns {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
@include breakpoint(medium) {
|
@include viewport.until(lg) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
@ -22,7 +24,7 @@
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include breakpoint(medium) {
|
@include viewport.until(lg) {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
|
@ -68,7 +70,7 @@
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
font-size: $font-down-1;
|
font-size: $font-down-1;
|
||||||
|
|
||||||
@include breakpoint(large) {
|
@include viewport.until(lg) {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
margin-bottom: 9px;
|
margin-bottom: 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (min-width: 1350px) {
|
@media all and (width >= 1350px) {
|
||||||
.address-fields {
|
.address-fields {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@discourse/lint-configs": "2.11.1",
|
"@discourse/lint-configs": "2.20.0",
|
||||||
"ember-template-lint": "7.0.1",
|
"ember-template-lint": "7.7.0",
|
||||||
"eslint": "9.22.0",
|
"eslint": "9.27.0",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"stylelint": "16.16.0"
|
"stylelint": "16.19.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 22",
|
"node": ">= 22",
|
||||||
|
|
830
pnpm-lock.yaml
830
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue