mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-23 18:53:36 +02:00
Doc: Add Joplin Cloud Teams offer to website
This commit is contained in:
parent
84d40b805e
commit
b3d09ce776
@ -1073,6 +1073,10 @@ footer .bottom-links-row p {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.joplin-cloud-feature-list table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.price-row .plan-type {
|
.price-row .plan-type {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="plan-price plan-price-monthly">
|
<div class="plan-price plan-price-monthly">
|
||||||
{{priceMonthly.formattedMonthlyAmount}}<sub class="per-month"> /month</sub>
|
{{priceMonthly.formattedMonthlyAmount}}<sub class="per-month"> /month{{#footnote}} (*){{/footnote}}</sub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="plan-price plan-price-yearly">
|
<div class="plan-price plan-price-yearly">
|
||||||
{{priceYearly.formattedMonthlyAmount}}<sub class="per-month"> /month</sub>
|
{{priceYearly.formattedMonthlyAmount}}<sub class="per-month"> /month{{#footnote}} (*){{/footnote}}</sub>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -20,17 +20,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#featuresOn}}
|
{{#featureLabelsOn}}
|
||||||
<p><i class="fas fa-check feature feature-on"></i>{{.}}</p>
|
<p><i class="fas fa-check feature feature-on"></i>{{.}}</p>
|
||||||
{{/featuresOn}}
|
{{/featureLabelsOn}}
|
||||||
|
|
||||||
{{#featuresOff}}
|
{{#featureLabelsOff}}
|
||||||
<p class="unchecked-text"><i class="fas fa-times feature feature-off"></i>{{.}}</p>
|
<p class="unchecked-text"><i class="fas fa-times feature feature-off"></i>{{.}}</p>
|
||||||
{{/featuresOff}}
|
{{/featureLabelsOff}}
|
||||||
|
|
||||||
<p class="text-center subscribe-wrapper">
|
<p class="text-center subscribe-wrapper">
|
||||||
<a id="subscribeButton-{{name}}" href="{{cfaUrl}}" class="button-link btn-white subscribeButton">{{cfaLabel}}</a>
|
<a id="subscribeButton-{{name}}" href="{{cfaUrl}}" class="button-link btn-white subscribeButton">{{cfaLabel}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{{#footnote}}<sub>(*) {{.}}</sub>{{/footnote}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -42,13 +42,23 @@
|
|||||||
{{> plan}}
|
{{> plan}}
|
||||||
{{/plans.pro}}
|
{{/plans.pro}}
|
||||||
|
|
||||||
{{#plans.business}}
|
{{#plans.teams}}
|
||||||
{{> plan}}
|
{{> plan}}
|
||||||
{{/plans.business}}
|
{{/plans.teams}}
|
||||||
|
|
||||||
<p class="joplin-cloud-login-info">Already have a Joplin Cloud account? <a href="https://joplincloud.com">Login now</a></p>
|
<p class="joplin-cloud-login-info">Already have a Joplin Cloud account? <a href="https://joplincloud.com">Login now</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
<h1>Feature comparison</h1>
|
||||||
|
<div class="joplin-cloud-feature-list">
|
||||||
|
{{{featureListHtml}}}
|
||||||
|
</div>
|
||||||
|
<p> </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{{faqHtml}}}
|
{{{faqHtml}}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,26 @@
|
|||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
|
import markdownUtils, { MarkdownTableHeader, MarkdownTableRow } from '../markdownUtils';
|
||||||
|
|
||||||
|
type FeatureId = string;
|
||||||
|
|
||||||
|
export enum PlanName {
|
||||||
|
Basic = 'basic',
|
||||||
|
Pro = 'pro',
|
||||||
|
Teams = 'teams',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlanFeature {
|
||||||
|
title: string;
|
||||||
|
basic: boolean;
|
||||||
|
pro: boolean;
|
||||||
|
teams: boolean;
|
||||||
|
basicInfo?: string;
|
||||||
|
proInfo?: string;
|
||||||
|
teamsInfo?: string;
|
||||||
|
basicInfoShort?: string;
|
||||||
|
proInfoShort?: string;
|
||||||
|
teamsInfoShort?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Plan {
|
export interface Plan {
|
||||||
name: string;
|
name: string;
|
||||||
@ -7,10 +29,13 @@ export interface Plan {
|
|||||||
priceYearly: StripePublicConfigPrice;
|
priceYearly: StripePublicConfigPrice;
|
||||||
featured: boolean;
|
featured: boolean;
|
||||||
iconName: string;
|
iconName: string;
|
||||||
featuresOn: string[];
|
featuresOn: FeatureId[];
|
||||||
featuresOff: string[];
|
featuresOff: FeatureId[];
|
||||||
|
featureLabelsOn: string[];
|
||||||
|
featureLabelsOff: string[];
|
||||||
cfaLabel: string;
|
cfaLabel: string;
|
||||||
cfaUrl: string;
|
cfaUrl: string;
|
||||||
|
footnote: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PricePeriod {
|
export enum PricePeriod {
|
||||||
@ -40,31 +65,6 @@ export interface StripePublicConfig {
|
|||||||
webhookBaseUrl: string;
|
webhookBaseUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlanFeature {
|
|
||||||
label: string;
|
|
||||||
enabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFeatureList(plan: Plan): PlanFeature[] {
|
|
||||||
const output: PlanFeature[] = [];
|
|
||||||
|
|
||||||
for (const f of plan.featuresOn) {
|
|
||||||
output.push({
|
|
||||||
label: f,
|
|
||||||
enabled: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const f of plan.featuresOff) {
|
|
||||||
output.push({
|
|
||||||
label: f,
|
|
||||||
enabled: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatPrice(amount: string | number, currency: PriceCurrency): string {
|
function formatPrice(amount: string | number, currency: PriceCurrency): string {
|
||||||
amount = typeof amount === 'number' ? (Math.ceil(amount * 100) / 100).toFixed(2) : amount;
|
amount = typeof amount === 'number' ? (Math.ceil(amount * 100) / 100).toFixed(2) : amount;
|
||||||
if (currency === PriceCurrency.EUR) return `${amount}€`;
|
if (currency === PriceCurrency.EUR) return `${amount}€`;
|
||||||
@ -110,28 +110,181 @@ export function findPrice(prices: StripePublicConfigPrice[], query: FindPriceQue
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
const businessAccountEmailBody = `Hello,
|
const features: Record<FeatureId, PlanFeature> = {
|
||||||
|
maxItemSize: {
|
||||||
|
title: 'Publish notes to the internet',
|
||||||
|
basic: true,
|
||||||
|
pro: true,
|
||||||
|
teams: true,
|
||||||
|
basicInfo: '10 MB per note or attachment',
|
||||||
|
proInfo: '200 MB per note or attachment',
|
||||||
|
teamsInfo: '200 MB per note or attachment',
|
||||||
|
basicInfoShort: '10 MB',
|
||||||
|
proInfoShort: '200 MB',
|
||||||
|
teamsInfoShort: '200 MB',
|
||||||
|
},
|
||||||
|
maxStorage: {
|
||||||
|
title: 'Storage space',
|
||||||
|
basic: true,
|
||||||
|
pro: true,
|
||||||
|
teams: true,
|
||||||
|
basicInfo: '1 GB storage space',
|
||||||
|
proInfo: '200 GB storage space',
|
||||||
|
teamsInfo: '200 GB storage space',
|
||||||
|
basicInfoShort: '1 GB',
|
||||||
|
proInfoShort: '200 GB',
|
||||||
|
teamsInfoShort: '200 GB',
|
||||||
|
},
|
||||||
|
publishNote: {
|
||||||
|
title: 'Publish notes to the internet',
|
||||||
|
basic: true,
|
||||||
|
pro: true,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
sync: {
|
||||||
|
title: 'Sync as many devices as you want',
|
||||||
|
basic: true,
|
||||||
|
pro: true,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
clipper: {
|
||||||
|
title: 'Web Clipper',
|
||||||
|
basic: true,
|
||||||
|
pro: true,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
collaborate: {
|
||||||
|
title: 'Share and collaborate on a notebook',
|
||||||
|
basic: false,
|
||||||
|
pro: true,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
multiUsers: {
|
||||||
|
title: 'Manage multiple users',
|
||||||
|
basic: false,
|
||||||
|
pro: false,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
consolidatedBilling: {
|
||||||
|
title: 'Consolidated billing',
|
||||||
|
basic: false,
|
||||||
|
pro: false,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
sharingAccessControl: {
|
||||||
|
title: 'Sharing access control',
|
||||||
|
basic: false,
|
||||||
|
pro: false,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
prioritySupport: {
|
||||||
|
title: 'Priority support',
|
||||||
|
basic: false,
|
||||||
|
pro: false,
|
||||||
|
teams: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
This is an automatically generated email. The Business feature is coming soon, and in the meantime we offer a business discount if you would like to register multiple users.
|
export const getFeatureIdsByPlan = (planName: PlanName, featureOn: boolean): FeatureId[] => {
|
||||||
|
const output: FeatureId[] = [];
|
||||||
|
|
||||||
If so please let us know the following details and we will get back to you as soon as possible:
|
for (const [k, v] of Object.entries(features)) {
|
||||||
|
if (v[planName] === featureOn) {
|
||||||
|
output.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- Name:
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
- Email:
|
export const getFeatureLabelsByPlan = (planName: PlanName, featureOn: boolean): string[] => {
|
||||||
|
const output: FeatureId[] = [];
|
||||||
|
|
||||||
- Number of users: `;
|
for (const [featureId, v] of Object.entries(features)) {
|
||||||
|
if (v[planName] === featureOn) {
|
||||||
|
output.push(getFeatureLabel(planName, featureId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getPlans(stripeConfig: StripePublicConfig): Record<string, Plan> {
|
return output;
|
||||||
const features = {
|
};
|
||||||
publishNote: 'Publish notes to the internet',
|
|
||||||
sync: 'Sync as many devices as you want',
|
export const getAllFeatureIds = (): FeatureId[] => {
|
||||||
clipper: 'Web Clipper',
|
return Object.keys(features);
|
||||||
collaborate: 'Share and collaborate on a notebook',
|
};
|
||||||
multiUsers: 'Up to 10 users',
|
|
||||||
prioritySupport: 'Priority support',
|
export const getFeatureById = (featureId: FeatureId): PlanFeature => {
|
||||||
|
return features[featureId];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFeaturesByPlan = (planName: PlanName, featureOn: boolean): PlanFeature[] => {
|
||||||
|
const output: PlanFeature[] = [];
|
||||||
|
|
||||||
|
for (const [, v] of Object.entries(features)) {
|
||||||
|
if (v[planName] === featureOn) {
|
||||||
|
output.push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFeatureLabel = (planName: PlanName, featureId: FeatureId): string => {
|
||||||
|
const feature = features[featureId];
|
||||||
|
const k = `${planName}Info`;
|
||||||
|
if ((feature as any)[k]) return (feature as any)[k];
|
||||||
|
return feature.title;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFeatureEnabled = (planName: PlanName, featureId: FeatureId): boolean => {
|
||||||
|
const feature = features[featureId];
|
||||||
|
return feature[planName];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createFeatureTableMd = () => {
|
||||||
|
const headers: MarkdownTableHeader[] = [
|
||||||
|
{
|
||||||
|
name: 'featureLabel',
|
||||||
|
label: 'Feature',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'basic',
|
||||||
|
label: 'Basic',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pro',
|
||||||
|
label: 'Pro',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'teams',
|
||||||
|
label: 'Teams',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const rows: MarkdownTableRow[] = [];
|
||||||
|
|
||||||
|
const getCellInfo = (planName: PlanName, feature: PlanFeature) => {
|
||||||
|
if (!feature[planName]) return '-';
|
||||||
|
const infoShort: string = (feature as any)[`${planName}InfoShort`];
|
||||||
|
if (infoShort) return infoShort;
|
||||||
|
return '✔️';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (const [, feature] of Object.entries(features)) {
|
||||||
|
const row: MarkdownTableRow = {
|
||||||
|
featureLabel: feature.title,
|
||||||
|
basic: getCellInfo(PlanName.Basic, feature),
|
||||||
|
pro: getCellInfo(PlanName.Pro, feature),
|
||||||
|
teams: getCellInfo(PlanName.Teams, feature),
|
||||||
|
};
|
||||||
|
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return markdownUtils.createMarkdownTable(headers, rows);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getPlans(stripeConfig: StripePublicConfig): Record<PlanName, Plan> {
|
||||||
return {
|
return {
|
||||||
basic: {
|
basic: {
|
||||||
name: 'basic',
|
name: 'basic',
|
||||||
@ -146,20 +299,13 @@ export function getPlans(stripeConfig: StripePublicConfig): Record<string, Plan>
|
|||||||
}),
|
}),
|
||||||
featured: false,
|
featured: false,
|
||||||
iconName: 'basic-icon',
|
iconName: 'basic-icon',
|
||||||
featuresOn: [
|
featuresOn: getFeatureIdsByPlan(PlanName.Basic, true),
|
||||||
'Max 10 MB per note or attachment',
|
featuresOff: getFeatureIdsByPlan(PlanName.Basic, false),
|
||||||
features.publishNote,
|
featureLabelsOn: getFeatureLabelsByPlan(PlanName.Basic, true),
|
||||||
features.sync,
|
featureLabelsOff: getFeatureLabelsByPlan(PlanName.Basic, false),
|
||||||
features.clipper,
|
|
||||||
'1 GB storage space',
|
|
||||||
],
|
|
||||||
featuresOff: [
|
|
||||||
features.collaborate,
|
|
||||||
features.multiUsers,
|
|
||||||
features.prioritySupport,
|
|
||||||
],
|
|
||||||
cfaLabel: 'Try it now',
|
cfaLabel: 'Try it now',
|
||||||
cfaUrl: '',
|
cfaUrl: '',
|
||||||
|
footnote: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
pro: {
|
pro: {
|
||||||
@ -175,42 +321,35 @@ export function getPlans(stripeConfig: StripePublicConfig): Record<string, Plan>
|
|||||||
}),
|
}),
|
||||||
featured: true,
|
featured: true,
|
||||||
iconName: 'pro-icon',
|
iconName: 'pro-icon',
|
||||||
featuresOn: [
|
featuresOn: getFeatureIdsByPlan(PlanName.Pro, true),
|
||||||
'Max 200 MB per note or attachment',
|
featuresOff: getFeatureIdsByPlan(PlanName.Pro, false),
|
||||||
features.publishNote,
|
featureLabelsOn: getFeatureLabelsByPlan(PlanName.Pro, true),
|
||||||
features.sync,
|
featureLabelsOff: getFeatureLabelsByPlan(PlanName.Pro, false),
|
||||||
features.clipper,
|
|
||||||
'10 GB storage space',
|
|
||||||
features.collaborate,
|
|
||||||
],
|
|
||||||
featuresOff: [
|
|
||||||
features.multiUsers,
|
|
||||||
features.prioritySupport,
|
|
||||||
],
|
|
||||||
cfaLabel: 'Try it now',
|
cfaLabel: 'Try it now',
|
||||||
cfaUrl: '',
|
cfaUrl: '',
|
||||||
|
footnote: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
business: {
|
teams: {
|
||||||
name: 'business',
|
name: 'teams',
|
||||||
title: 'Business',
|
title: 'Teams',
|
||||||
priceMonthly: { accountType: 3, formattedMonthlyAmount: '49.99€' } as any,
|
priceMonthly: findPrice(stripeConfig.prices, {
|
||||||
priceYearly: { accountType: 3, formattedMonthlyAmount: '39.99€', formattedAmount: '479.88€' } as any,
|
accountType: 3,
|
||||||
|
period: PricePeriod.Monthly,
|
||||||
|
}),
|
||||||
|
priceYearly: findPrice(stripeConfig.prices, {
|
||||||
|
accountType: 3,
|
||||||
|
period: PricePeriod.Yearly,
|
||||||
|
}),
|
||||||
featured: false,
|
featured: false,
|
||||||
iconName: 'business-icon',
|
iconName: 'business-icon',
|
||||||
featuresOn: [
|
featuresOn: getFeatureIdsByPlan(PlanName.Teams, true),
|
||||||
'Max 200 MB per note or attachment',
|
featuresOff: getFeatureIdsByPlan(PlanName.Teams, false),
|
||||||
features.publishNote,
|
featureLabelsOn: getFeatureLabelsByPlan(PlanName.Teams, true),
|
||||||
features.sync,
|
featureLabelsOff: getFeatureLabelsByPlan(PlanName.Teams, false),
|
||||||
features.clipper,
|
cfaLabel: 'Try it now',
|
||||||
'10 GB storage space',
|
cfaUrl: '',
|
||||||
features.collaborate,
|
footnote: 'Per user. Minimum of 2 users.',
|
||||||
features.multiUsers,
|
|
||||||
features.prioritySupport,
|
|
||||||
],
|
|
||||||
featuresOff: [],
|
|
||||||
cfaLabel: 'Contact us',
|
|
||||||
cfaUrl: `mailto:business@joplincloud.com?subject=${encodeURIComponent('Joplin Cloud Business Account Order')}&body=${encodeURIComponent(businessAccountEmailBody)}`,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { SubPath, redirect } from '../../utils/routeUtils';
|
|||||||
import Router from '../../utils/Router';
|
import Router from '../../utils/Router';
|
||||||
import { RouteType } from '../../utils/types';
|
import { RouteType } from '../../utils/types';
|
||||||
import { AppContext } from '../../utils/types';
|
import { AppContext } from '../../utils/types';
|
||||||
import { findPrice, getFeatureList, getPlans, PricePeriod } from '@joplin/lib/utils/joplinCloud';
|
import { findPrice, PricePeriod, PlanName, getFeatureLabel, getFeatureEnabled, getAllFeatureIds } from '@joplin/lib/utils/joplinCloud';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import defaultView from '../../utils/defaultView';
|
import defaultView from '../../utils/defaultView';
|
||||||
import { stripeConfig, stripePriceIdByUserId, updateSubscriptionType } from '../../utils/stripe';
|
import { stripeConfig, stripePriceIdByUserId, updateSubscriptionType } from '../../utils/stripe';
|
||||||
@ -28,21 +28,23 @@ router.get('upgrade', async (_path: SubPath, ctx: AppContext) => {
|
|||||||
proLabel: string;
|
proLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const plans = getPlans(stripeConfig());
|
const featureIds = getAllFeatureIds();
|
||||||
const basicFeatureList = getFeatureList(plans.basic);
|
|
||||||
const proFeatureList = getFeatureList(plans.pro);
|
|
||||||
|
|
||||||
const planRows: PlanRow[] = [];
|
const planRows: PlanRow[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < basicFeatureList.length; i++) {
|
for (let i = 0; i < featureIds.length; i++) {
|
||||||
const basic = basicFeatureList[i];
|
const featureId = featureIds[i];
|
||||||
const pro = proFeatureList[i];
|
|
||||||
|
|
||||||
if (basic.label === pro.label && basic.enabled === pro.enabled) continue;
|
const basicLabel = getFeatureLabel(PlanName.Basic, featureId);
|
||||||
|
const proLabel = getFeatureLabel(PlanName.Pro, featureId);
|
||||||
|
const basicEnabled = getFeatureEnabled(PlanName.Basic, featureId);
|
||||||
|
const proEnabled = getFeatureEnabled(PlanName.Pro, featureId);
|
||||||
|
|
||||||
|
if (basicLabel === proLabel && basicEnabled === proEnabled) continue;
|
||||||
|
|
||||||
planRows.push({
|
planRows.push({
|
||||||
basicLabel: basic.enabled ? basic.label : '-',
|
basicLabel: basicEnabled ? basicLabel : '-',
|
||||||
proLabel: pro.label,
|
proLabel: proLabel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,20 @@
|
|||||||
"period": "yearly",
|
"period": "yearly",
|
||||||
"amount": "57.48",
|
"amount": "57.48",
|
||||||
"currency": "EUR"
|
"currency": "EUR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountType": 3,
|
||||||
|
"id": "price_1Kl9uBLx4fybOTqJhx7q4zzj",
|
||||||
|
"period": "monthly",
|
||||||
|
"amount": "7.99",
|
||||||
|
"currency": "EUR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountType": 3,
|
||||||
|
"id": "price_1Kl9uNLx4fybOTqJpsB2l3Kg",
|
||||||
|
"period": "yearly",
|
||||||
|
"amount": "80.28",
|
||||||
|
"currency": "EUR"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -64,6 +78,20 @@
|
|||||||
"period": "yearly",
|
"period": "yearly",
|
||||||
"amount": "57.48",
|
"amount": "57.48",
|
||||||
"currency": "EUR"
|
"currency": "EUR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountType": 3,
|
||||||
|
"id": "price_1Kl9jyLx4fybOTqJN0i1A88B",
|
||||||
|
"period": "monthly",
|
||||||
|
"amount": "7.99",
|
||||||
|
"currency": "EUR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountType": 3,
|
||||||
|
"id": "price_1Kl9nLLx4fybOTqJYTtts35z",
|
||||||
|
"period": "yearly",
|
||||||
|
"amount": "80.28",
|
||||||
|
"currency": "EUR"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { rootDir } from '../tool-utils';
|
|||||||
import { pressCarouselItems } from './utils/pressCarousel';
|
import { pressCarouselItems } from './utils/pressCarousel';
|
||||||
import { getMarkdownIt, loadMustachePartials, markdownToPageHtml, renderMustache } from './utils/render';
|
import { getMarkdownIt, loadMustachePartials, markdownToPageHtml, renderMustache } from './utils/render';
|
||||||
import { AssetUrls, Env, PlanPageParams, Sponsors, TemplateParams } from './utils/types';
|
import { AssetUrls, Env, PlanPageParams, Sponsors, TemplateParams } from './utils/types';
|
||||||
import { getPlans, loadStripeConfig } from '@joplin/lib/utils/joplinCloud';
|
import { createFeatureTableMd, getPlans, loadStripeConfig } from '@joplin/lib/utils/joplinCloud';
|
||||||
import { MarkdownAndFrontMatter, stripOffFrontMatter } from './utils/frontMatter';
|
import { MarkdownAndFrontMatter, stripOffFrontMatter } from './utils/frontMatter';
|
||||||
import { dirname, basename } from 'path';
|
import { dirname, basename } from 'path';
|
||||||
import { readmeFileTitle, replaceGitHubByWebsiteLinks } from './utils/parser';
|
import { readmeFileTitle, replaceGitHubByWebsiteLinks } from './utils/parser';
|
||||||
@ -14,7 +14,7 @@ const glob = require('glob');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const md5File = require('md5-file/promise');
|
const md5File = require('md5-file/promise');
|
||||||
|
|
||||||
const env = Env.Prod;
|
const env = Env.Dev;
|
||||||
|
|
||||||
const docDir = `${dirname(dirname(dirname(dirname(__dirname))))}/joplin-website/docs`;
|
const docDir = `${dirname(dirname(dirname(dirname(__dirname))))}/joplin-website/docs`;
|
||||||
|
|
||||||
@ -281,6 +281,7 @@ async function main() {
|
|||||||
templateHtml: plansTemplateHtml,
|
templateHtml: plansTemplateHtml,
|
||||||
plans: getPlans(stripeConfig),
|
plans: getPlans(stripeConfig),
|
||||||
faqHtml: planPageFaqHtml,
|
faqHtml: planPageFaqHtml,
|
||||||
|
featureListHtml: getMarkdownIt().render(createFeatureTableMd(), {}),
|
||||||
stripeConfig,
|
stripeConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,15 +24,11 @@ export function renderMustache(contentHtml: string, templateParams: TemplatePara
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getMarkdownIt() {
|
export function getMarkdownIt() {
|
||||||
return new MarkdownIt({
|
const markdownIt = new MarkdownIt({
|
||||||
breaks: true,
|
breaks: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
html: true,
|
html: true,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
export function markdownToPageHtml(md: string, templateParams: TemplateParams): string {
|
|
||||||
const markdownIt = getMarkdownIt();
|
|
||||||
|
|
||||||
markdownIt.core.ruler.push('tableClass', (state: any) => {
|
markdownIt.core.ruler.push('tableClass', (state: any) => {
|
||||||
const tokens = state.tokens;
|
const tokens = state.tokens;
|
||||||
@ -47,7 +43,11 @@ export function markdownToPageHtml(md: string, templateParams: TemplateParams):
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
markdownIt.use(headerAnchor);
|
return markdownIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markdownToPageHtml(md: string, templateParams: TemplateParams): string {
|
||||||
|
const markdownIt = getMarkdownIt();
|
||||||
|
markdownIt.use(headerAnchor);
|
||||||
return renderMustache(markdownIt.render(md), templateParams);
|
return renderMustache(markdownIt.render(md), templateParams);
|
||||||
}
|
}
|
||||||
|
@ -79,5 +79,6 @@ export interface TemplateParams {
|
|||||||
export interface PlanPageParams extends TemplateParams {
|
export interface PlanPageParams extends TemplateParams {
|
||||||
plans: Record<string, Plan>;
|
plans: Record<string, Plan>;
|
||||||
faqHtml: string;
|
faqHtml: string;
|
||||||
|
featureListHtml: string;
|
||||||
stripeConfig: StripePublicConfig;
|
stripeConfig: StripePublicConfig;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user