Browse Source

Feature/align pricing page with subscription model (#135)

* Align pricing page with subscription model

* Update changelog
pull/130/head
Thomas 4 years ago
parent
commit
6062bdbeab
  1. 2
      CHANGELOG.md
  2. 52
      apps/api/src/app/user/user.service.ts
  3. 8
      apps/client/src/app/pages/account/account-page.component.ts
  4. 6
      apps/client/src/app/pages/account/account-page.html
  5. 5
      libs/common/src/lib/interfaces/user-with-settings.ts
  6. 4
      libs/common/src/lib/types/subscription.type.ts
  7. 12
      prisma/schema.prisma

2
CHANGELOG.md

@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Moved the tools to a sub path (`/tools`) - Moved the tools to a sub path (`/tools`)
- Extended the pricing page - Extended the pricing page and aligned with the subscription model
## 1.9.0 - 01.06.2021 ## 1.9.0 - 01.06.2021

52
apps/api/src/app/user/user.service.ts

@ -4,9 +4,10 @@ import { locale } from '@ghostfolio/common/config';
import { resetHours } from '@ghostfolio/common/helper'; import { resetHours } from '@ghostfolio/common/helper';
import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces'; import { User as IUser, UserWithSettings } from '@ghostfolio/common/interfaces';
import { getPermissions, permissions } from '@ghostfolio/common/permissions'; import { getPermissions, permissions } from '@ghostfolio/common/permissions';
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Currency, Prisma, Provider, User, ViewMode } from '@prisma/client'; import { Currency, Prisma, Provider, User, ViewMode } from '@prisma/client';
import { add } from 'date-fns'; import { add, isBefore } from 'date-fns';
const crypto = require('crypto'); const crypto = require('crypto');
@ -24,7 +25,8 @@ export class UserService {
alias, alias,
id, id,
role, role,
Settings Settings,
subscription
}: UserWithSettings): Promise<IUser> { }: UserWithSettings): Promise<IUser> {
const access = await this.prisma.access.findMany({ const access = await this.prisma.access.findMany({
include: { include: {
@ -43,6 +45,7 @@ export class UserService {
return { return {
alias, alias,
id, id,
subscription,
access: access.map((accessItem) => { access: access.map((accessItem) => {
return { return {
alias: accessItem.User.alias, alias: accessItem.User.alias,
@ -54,11 +57,7 @@ export class UserService {
settings: { settings: {
locale, locale,
baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY, baseCurrency: Settings?.currency ?? UserService.DEFAULT_CURRENCY,
viewMode: Settings.viewMode ?? ViewMode.DEFAULT viewMode: Settings?.viewMode ?? ViewMode.DEFAULT
},
subscription: {
expiresAt: resetHours(add(new Date(), { days: 7 })),
type: 'Premium'
} }
}; };
} }
@ -66,26 +65,49 @@ export class UserService {
public async user( public async user(
userWhereUniqueInput: Prisma.UserWhereUniqueInput userWhereUniqueInput: Prisma.UserWhereUniqueInput
): Promise<UserWithSettings | null> { ): Promise<UserWithSettings | null> {
const user = await this.prisma.user.findUnique({ const userFromDatabase = await this.prisma.user.findUnique({
include: { Account: true, Settings: true }, include: { Account: true, Settings: true, Subscription: true },
where: userWhereUniqueInput where: userWhereUniqueInput
}); });
if (user?.Settings) { const user: UserWithSettings = userFromDatabase;
if (!user.Settings.currency) {
if (userFromDatabase?.Settings) {
if (!userFromDatabase.Settings.currency) {
// Set default currency if needed // Set default currency if needed
user.Settings.currency = UserService.DEFAULT_CURRENCY; userFromDatabase.Settings.currency = UserService.DEFAULT_CURRENCY;
} }
} else if (user) { } else if (userFromDatabase) {
// Set default settings if needed // Set default settings if needed
user.Settings = { userFromDatabase.Settings = {
currency: UserService.DEFAULT_CURRENCY, currency: UserService.DEFAULT_CURRENCY,
updatedAt: new Date(), updatedAt: new Date(),
userId: user?.id, userId: userFromDatabase?.id,
viewMode: ViewMode.DEFAULT viewMode: ViewMode.DEFAULT
}; };
} }
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
if (userFromDatabase?.Subscription?.length > 0) {
const latestSubscription = userFromDatabase.Subscription.reduce(
(a, b) => {
return new Date(a.expiresAt) > new Date(b.expiresAt) ? a : b;
}
);
user.subscription = {
expiresAt: latestSubscription.expiresAt,
type: isBefore(new Date(), latestSubscription.expiresAt)
? SubscriptionType.Premium
: SubscriptionType.Basic
};
} else {
user.subscription = {
type: SubscriptionType.Basic
};
}
}
return user; return user;
} }

8
apps/client/src/app/pages/account/account-page.component.ts

@ -18,7 +18,6 @@ export class AccountPageComponent implements OnDestroy, OnInit {
public baseCurrency: Currency; public baseCurrency: Currency;
public currencies: Currency[] = []; public currencies: Currency[] = [];
public defaultDateFormat = DEFAULT_DATE_FORMAT; public defaultDateFormat = DEFAULT_DATE_FORMAT;
public hasPermissionForSubscription: boolean;
public hasPermissionToUpdateUserSettings: boolean; public hasPermissionToUpdateUserSettings: boolean;
public user: User; public user: User;
@ -35,13 +34,8 @@ export class AccountPageComponent implements OnDestroy, OnInit {
this.dataService this.dataService
.fetchInfo() .fetchInfo()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currencies, globalPermissions }) => { .subscribe(({ currencies }) => {
this.currencies = currencies; this.currencies = currencies;
this.hasPermissionForSubscription = hasPermission(
globalPermissions,
permissions.enableSubscription
);
}); });
this.userService.stateChanged this.userService.stateChanged

6
apps/client/src/app/pages/account/account-page.html

@ -15,13 +15,13 @@
<div class="w-50" i18n>Alias</div> <div class="w-50" i18n>Alias</div>
<div class="w-50">{{ user.alias }}</div> <div class="w-50">{{ user.alias }}</div>
</div> </div>
<div *ngIf="hasPermissionForSubscription" class="d-flex py-1"> <div *ngIf="user?.subscription" class="d-flex py-1">
<div class="w-50" i18n>Membership</div> <div class="w-50" i18n>Membership</div>
<div class="w-50"> <div class="w-50">
<div class="align-items-center d-flex mb-1"> <div class="align-items-center d-flex mb-1">
{{ user?.subscription?.type }} {{ user.subscription.type }}
</div> </div>
<div> <div *ngIf="user.subscription.expiresAt">
Valid until {{ user.subscription.expiresAt | date: Valid until {{ user.subscription.expiresAt | date:
defaultDateFormat }} defaultDateFormat }}
</div> </div>

5
libs/common/src/lib/interfaces/user-with-settings.ts

@ -1,6 +1,11 @@
import { SubscriptionType } from '@ghostfolio/common/types/subscription.type';
import { Account, Settings, User } from '@prisma/client'; import { Account, Settings, User } from '@prisma/client';
export type UserWithSettings = User & { export type UserWithSettings = User & {
Account: Account[]; Account: Account[];
Settings: Settings; Settings: Settings;
subscription?: {
expiresAt?: Date;
type: SubscriptionType;
};
}; };

4
libs/common/src/lib/types/subscription.type.ts

@ -0,0 +1,4 @@
export enum SubscriptionType {
Basic = 'Basic',
Premium = 'Premium'
}

12
prisma/schema.prisma

@ -99,6 +99,17 @@ model Settings {
userId String @id userId String @id
} }
model Subscription {
createdAt DateTime @default(now())
expiresAt DateTime
id String @default(uuid())
updatedAt DateTime @updatedAt
User User @relation(fields: [userId], references: [id])
userId String
@@id([id, userId])
}
model User { model User {
Access Access[] @relation("accessGet") Access Access[] @relation("accessGet")
AccessGive Access[] @relation(name: "accessGive") AccessGive Access[] @relation(name: "accessGive")
@ -112,6 +123,7 @@ model User {
provider Provider? provider Provider?
role Role @default(USER) role Role @default(USER)
Settings Settings? Settings Settings?
Subscription Subscription[]
thirdPartyId String? thirdPartyId String?
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
} }

Loading…
Cancel
Save