From 5f56812125e354cc187e953d74f1c4a884a8127a Mon Sep 17 00:00:00 2001
From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com>
Date: Sat, 2 Nov 2024 13:44:57 +0100
Subject: [PATCH] Feature/extend promotion system (#4008)
* Extend promotion system
---
apps/api/src/app/info/info.service.ts | 14 ++---
.../app/subscription/subscription.service.ts | 57 +++++++++++++++++--
apps/client/src/app/app.component.html | 1 +
apps/client/src/app/app.component.ts | 13 +++++
.../components/header/header.component.html | 27 +++++++--
.../app/components/header/header.component.ts | 1 +
.../user-account-membership.component.ts | 19 +++++--
.../user-account-membership.html | 10 ++++
.../pages/pricing/pricing-page.component.ts | 24 +++++---
.../src/app/pages/pricing/pricing-page.html | 18 ++++++
.../product-page.component.ts | 4 +-
libs/common/src/lib/interfaces/index.ts | 4 +-
.../src/lib/interfaces/info-item.interface.ts | 6 +-
.../subscription-offer.interface.ts | 9 +++
.../lib/interfaces/subscription.interface.ts | 6 --
.../src/lib/interfaces/user.interface.ts | 4 +-
libs/common/src/lib/types/index.ts | 4 +-
...type.ts => subscription-offer-key.type.ts} | 2 +-
.../src/lib/types/user-with-settings.type.ts | 4 +-
19 files changed, 177 insertions(+), 50 deletions(-)
create mode 100644 libs/common/src/lib/interfaces/subscription-offer.interface.ts
delete mode 100644 libs/common/src/lib/interfaces/subscription.interface.ts
rename libs/common/src/lib/types/{subscription-offer.type.ts => subscription-offer-key.type.ts} (71%)
diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts
index bd291c511..f81ddd710 100644
--- a/apps/api/src/app/info/info.service.ts
+++ b/apps/api/src/app/info/info.service.ts
@@ -23,10 +23,10 @@ import {
import {
InfoItem,
Statistics,
- Subscription
+ SubscriptionOffer
} from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions';
-import { SubscriptionOffer } from '@ghostfolio/common/types';
+import { SubscriptionOfferKey } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@@ -101,7 +101,7 @@ export class InfoService {
isUserSignupEnabled,
platforms,
statistics,
- subscriptions
+ subscriptionOffers
] = await Promise.all([
this.benchmarkService.getBenchmarkAssetProfiles(),
this.getDemoAuthToken(),
@@ -110,7 +110,7 @@ export class InfoService {
orderBy: { name: 'asc' }
}),
this.getStatistics(),
- this.getSubscriptions()
+ this.getSubscriptionOffers()
]);
if (isUserSignupEnabled) {
@@ -125,7 +125,7 @@ export class InfoService {
isReadOnlyMode,
platforms,
statistics,
- subscriptions,
+ subscriptionOffers,
baseCurrency: DEFAULT_CURRENCY,
currencies: this.exchangeRateDataService.getCurrencies()
};
@@ -314,8 +314,8 @@ export class InfoService {
return statistics;
}
- private async getSubscriptions(): Promise<{
- [offer in SubscriptionOffer]: Subscription;
+ private async getSubscriptionOffers(): Promise<{
+ [offer in SubscriptionOfferKey]: SubscriptionOffer;
}> {
if (!this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
return undefined;
diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts
index 7c1df023c..47e6db00d 100644
--- a/apps/api/src/app/subscription/subscription.service.ts
+++ b/apps/api/src/app/subscription/subscription.service.ts
@@ -1,8 +1,16 @@
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
-import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config';
+import { PropertyService } from '@ghostfolio/api/services/property/property.service';
+import {
+ DEFAULT_LANGUAGE_CODE,
+ PROPERTY_STRIPE_CONFIG
+} from '@ghostfolio/common/config';
import { parseDate } from '@ghostfolio/common/helper';
-import { SubscriptionOffer, UserWithSettings } from '@ghostfolio/common/types';
+import { SubscriptionOffer } from '@ghostfolio/common/interfaces';
+import {
+ SubscriptionOfferKey,
+ UserWithSettings
+} from '@ghostfolio/common/types';
import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type';
import { Injectable, Logger } from '@nestjs/common';
@@ -17,7 +25,8 @@ export class SubscriptionService {
public constructor(
private readonly configurationService: ConfigurationService,
- private readonly prismaService: PrismaService
+ private readonly prismaService: PrismaService,
+ private readonly propertyService: PropertyService
) {
this.stripe = new Stripe(
this.configurationService.get('STRIPE_SECRET_KEY'),
@@ -36,6 +45,18 @@ export class SubscriptionService {
priceId: string;
user: UserWithSettings;
}) {
+ const subscriptionOffers: {
+ [offer in SubscriptionOfferKey]: SubscriptionOffer;
+ } =
+ ((await this.propertyService.getByKey(PROPERTY_STRIPE_CONFIG)) as any) ??
+ {};
+
+ const subscriptionOffer = Object.values(subscriptionOffers).find(
+ (subscriptionOffer) => {
+ return subscriptionOffer.priceId === priceId;
+ }
+ );
+
const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = {
cancel_url: `${this.configurationService.get('ROOT_URL')}/${
user.Settings?.settings?.language ?? DEFAULT_LANGUAGE_CODE
@@ -47,6 +68,13 @@ export class SubscriptionService {
quantity: 1
}
],
+ locale:
+ (user.Settings?.settings
+ ?.language as Stripe.Checkout.SessionCreateParams.Locale) ??
+ DEFAULT_LANGUAGE_CODE,
+ metadata: subscriptionOffer
+ ? { subscriptionOffer: JSON.stringify(subscriptionOffer) }
+ : {},
mode: 'payment',
payment_method_types: ['card'],
success_url: `${this.configurationService.get(
@@ -73,17 +101,25 @@ export class SubscriptionService {
public async createSubscription({
duration = '1 year',
+ durationExtension,
price,
userId
}: {
duration?: StringValue;
+ durationExtension?: StringValue;
price: number;
userId: string;
}) {
+ let expiresAt = addMilliseconds(new Date(), ms(duration));
+
+ if (durationExtension) {
+ expiresAt = addMilliseconds(expiresAt, ms(durationExtension));
+ }
+
await this.prismaService.subscription.create({
data: {
+ expiresAt,
price,
- expiresAt: addMilliseconds(new Date(), ms(duration)),
User: {
connect: {
id: userId
@@ -95,10 +131,21 @@ export class SubscriptionService {
public async createSubscriptionViaStripe(aCheckoutSessionId: string) {
try {
+ let durationExtension: StringValue;
+
const session =
await this.stripe.checkout.sessions.retrieve(aCheckoutSessionId);
+ const subscriptionOffer: SubscriptionOffer = JSON.parse(
+ session.metadata.subscriptionOffer ?? '{}'
+ );
+
+ if (subscriptionOffer) {
+ durationExtension = subscriptionOffer.durationExtension;
+ }
+
await this.createSubscription({
+ durationExtension,
price: session.amount_total / 100,
userId: session.client_reference_id
});
@@ -121,7 +168,7 @@ export class SubscriptionService {
return new Date(a.expiresAt) > new Date(b.expiresAt) ? a : b;
});
- let offer: SubscriptionOffer = price ? 'renewal' : 'default';
+ let offer: SubscriptionOfferKey = price ? 'renewal' : 'default';
if (isBefore(createdAt, parseDate('2023-01-01'))) {
offer = 'renewal-early-bird-2023';
diff --git a/apps/client/src/app/app.component.html b/apps/client/src/app/app.component.html
index b12855488..7560e15e5 100644
--- a/apps/client/src/app/app.component.html
+++ b/apps/client/src/app/app.component.html
@@ -33,6 +33,7 @@
[deviceType]="deviceType"
[hasPermissionToChangeDateRange]="hasPermissionToChangeDateRange"
[hasPermissionToChangeFilters]="hasPermissionToChangeFilters"
+ [hasPromotion]="hasPromotion"
[hasTabs]="hasTabs"
[info]="info"
[pageTitle]="pageTitle"
diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts
index 75841686c..86d4282a2 100644
--- a/apps/client/src/app/app.component.ts
+++ b/apps/client/src/app/app.component.ts
@@ -57,6 +57,7 @@ export class AppComponent implements OnDestroy, OnInit {
public hasPermissionToAccessFearAndGreedIndex: boolean;
public hasPermissionToChangeDateRange: boolean;
public hasPermissionToChangeFilters: boolean;
+ public hasPromotion = false;
public hasTabs = false;
public info: InfoItem;
public pageTitle: string;
@@ -136,6 +137,10 @@ export class AppComponent implements OnDestroy, OnInit {
permissions.enableFearAndGreedIndex
);
+ this.hasPromotion =
+ !!this.info?.subscriptionOffers?.default?.coupon ||
+ !!this.info?.subscriptionOffers?.default?.durationExtension;
+
this.impersonationStorageService
.onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject))
@@ -231,6 +236,14 @@ export class AppComponent implements OnDestroy, OnInit {
this.hasInfoMessage =
this.canCreateAccount || !!this.user?.systemMessage;
+ this.hasPromotion =
+ !!this.info?.subscriptionOffers?.[
+ this.user?.subscription?.offer ?? 'default'
+ ]?.coupon ||
+ !!this.info?.subscriptionOffers?.[
+ this.user?.subscription?.offer ?? 'default'
+ ]?.durationExtension;
+
this.initializeTheme(this.user?.settings.colorScheme);
this.changeDetectorRef.markForCheck();
diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html
index ff36c2ebe..8a611d935 100644
--- a/apps/client/src/app/components/header/header.component.html
+++ b/apps/client/src/app/components/header/header.component.html
@@ -88,15 +88,20 @@
Pricing
+
+ Pricing
+ @if (currentRoute !== routePricing && hasPromotion) {
+ %
+ }
+
+
}
@@ -290,12 +295,17 @@
) {
Pricing
+
+ Pricing
+ @if (currentRoute !== routePricing && hasPromotion) {
+ %
+ }
+
+
}
Pricing
+
+ Pricing
+ @if (currentRoute !== routePricing && hasPromotion) {
+ %
+ }
+
+
}
@if (hasPermissionToAccessFearAndGreedIndex) {
diff --git a/apps/client/src/app/components/header/header.component.ts b/apps/client/src/app/components/header/header.component.ts
index 33069aa23..1739d113f 100644
--- a/apps/client/src/app/components/header/header.component.ts
+++ b/apps/client/src/app/components/header/header.component.ts
@@ -58,6 +58,7 @@ export class HeaderComponent implements OnChanges {
@Input() deviceType: string;
@Input() hasPermissionToChangeDateRange: boolean;
@Input() hasPermissionToChangeFilters: boolean;
+ @Input() hasPromotion: boolean;
@Input() hasTabs: boolean;
@Input() info: InfoItem;
@Input() pageTitle: string;
diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts b/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
index 93bbe641c..bde555d8e 100644
--- a/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
+++ b/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts
@@ -16,6 +16,7 @@ import {
MatSnackBarRef,
TextOnlySnackBar
} from '@angular/material/snack-bar';
+import { StringValue } from 'ms';
import { StripeService } from 'ngx-stripe';
import { EMPTY, Subject } from 'rxjs';
import { catchError, switchMap, takeUntil } from 'rxjs/operators';
@@ -31,6 +32,7 @@ export class UserAccountMembershipComponent implements OnDestroy {
public coupon: number;
public couponId: string;
public defaultDateFormat: string;
+ public durationExtension: StringValue;
public hasPermissionForSubscription: boolean;
public hasPermissionToUpdateUserSettings: boolean;
public price: number;
@@ -51,7 +53,7 @@ export class UserAccountMembershipComponent implements OnDestroy {
private stripeService: StripeService,
private userService: UserService
) {
- const { baseCurrency, globalPermissions, subscriptions } =
+ const { baseCurrency, globalPermissions, subscriptionOffers } =
this.dataService.fetchInfo();
this.baseCurrency = baseCurrency;
@@ -76,11 +78,18 @@ export class UserAccountMembershipComponent implements OnDestroy {
permissions.updateUserSettings
);
- this.coupon = subscriptions?.[this.user.subscription.offer]?.coupon;
+ this.coupon =
+ subscriptionOffers?.[this.user.subscription.offer]?.coupon;
this.couponId =
- subscriptions?.[this.user.subscription.offer]?.couponId;
- this.price = subscriptions?.[this.user.subscription.offer]?.price;
- this.priceId = subscriptions?.[this.user.subscription.offer]?.priceId;
+ subscriptionOffers?.[this.user.subscription.offer]?.couponId;
+ this.durationExtension =
+ subscriptionOffers?.[
+ this.user.subscription.offer
+ ]?.durationExtension;
+ this.price =
+ subscriptionOffers?.[this.user.subscription.offer]?.price;
+ this.priceId =
+ subscriptionOffers?.[this.user.subscription.offer]?.priceId;
this.changeDetectorRef.markForCheck();
}
diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.html b/apps/client/src/app/components/user-account-membership/user-account-membership.html
index d30ce7bdd..82b329a64 100644
--- a/apps/client/src/app/components/user-account-membership/user-account-membership.html
+++ b/apps/client/src/app/components/user-account-membership/user-account-membership.html
@@ -34,6 +34,16 @@
per year
}
+ @if (durationExtension) {
+
+
+ Limited Offer! Get
+ {{ durationExtension }} extra
+
+
+ }
}
@if (!user?.subscription?.expiresAt) {
diff --git a/apps/client/src/app/pages/pricing/pricing-page.component.ts b/apps/client/src/app/pages/pricing/pricing-page.component.ts
index 8bd0f1bd5..f86a75904 100644
--- a/apps/client/src/app/pages/pricing/pricing-page.component.ts
+++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts
@@ -6,6 +6,7 @@ import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { translate } from '@ghostfolio/ui/i18n';
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
+import { StringValue } from 'ms';
import { StripeService } from 'ngx-stripe';
import { Subject } from 'rxjs';
import { catchError, switchMap, takeUntil } from 'rxjs/operators';
@@ -20,6 +21,7 @@ export class PricingPageComponent implements OnDestroy, OnInit {
public baseCurrency: string;
public coupon: number;
public couponId: string;
+ public durationExtension: StringValue;
public hasPermissionToUpdateUserSettings: boolean;
public importAndExportTooltipBasic = translate(
'DATA_IMPORT_AND_EXPORT_TOOLTIP_BASIC'
@@ -51,11 +53,12 @@ export class PricingPageComponent implements OnDestroy, OnInit {
) {}
public ngOnInit() {
- const { baseCurrency, subscriptions } = this.dataService.fetchInfo();
+ const { baseCurrency, subscriptionOffers } = this.dataService.fetchInfo();
this.baseCurrency = baseCurrency;
- this.coupon = subscriptions?.default?.coupon;
- this.price = subscriptions?.default?.price;
+ this.coupon = subscriptionOffers?.default?.coupon;
+ this.durationExtension = subscriptionOffers?.default?.durationExtension;
+ this.price = subscriptionOffers?.default?.price;
this.userService.stateChanged
.pipe(takeUntil(this.unsubscribeSubject))
@@ -68,11 +71,18 @@ export class PricingPageComponent implements OnDestroy, OnInit {
permissions.updateUserSettings
);
- this.coupon = subscriptions?.[this.user?.subscription?.offer]?.coupon;
+ this.coupon =
+ subscriptionOffers?.[this.user?.subscription?.offer]?.coupon;
this.couponId =
- subscriptions?.[this.user.subscription.offer]?.couponId;
- this.price = subscriptions?.[this.user?.subscription?.offer]?.price;
- this.priceId = subscriptions?.[this.user.subscription.offer]?.priceId;
+ subscriptionOffers?.[this.user.subscription.offer]?.couponId;
+ this.durationExtension =
+ subscriptionOffers?.[
+ this.user?.subscription?.offer
+ ]?.durationExtension;
+ this.price =
+ subscriptionOffers?.[this.user?.subscription?.offer]?.price;
+ this.priceId =
+ subscriptionOffers?.[this.user.subscription.offer]?.priceId;
this.changeDetectorRef.markForCheck();
}
diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html
index fe805ef62..605ad5d2e 100644
--- a/apps/client/src/app/pages/pricing/pricing-page.html
+++ b/apps/client/src/app/pages/pricing/pricing-page.html
@@ -101,6 +101,11 @@
}
+ @if (durationExtension) {
+
+ }
@@ -159,6 +164,11 @@
}
+ @if (durationExtension) {
+
+ }
@@ -289,6 +299,14 @@
}
+ @if (durationExtension) {
+
+
+ Limited Offer! Get
+ {{ durationExtension }} extra
+
+
+ }
diff --git a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts
index ea14bbc6b..39dbc4813 100644
--- a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts
+++ b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts
@@ -35,9 +35,9 @@ export class GfProductPageComponent implements OnInit {
) {}
public ngOnInit() {
- const { subscriptions } = this.dataService.fetchInfo();
+ const { subscriptionOffers } = this.dataService.fetchInfo();
- this.price = subscriptions?.default?.price;
+ this.price = subscriptionOffers?.default?.price;
this.product1 = {
founded: 2021,
diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts
index 0ec04594c..becc872dd 100644
--- a/libs/common/src/lib/interfaces/index.ts
+++ b/libs/common/src/lib/interfaces/index.ts
@@ -46,7 +46,7 @@ import type { PortfolioPerformanceResponse } from './responses/portfolio-perform
import type { PublicPortfolioResponse } from './responses/public-portfolio-response.interface';
import type { ScraperConfiguration } from './scraper-configuration.interface';
import type { Statistics } from './statistics.interface';
-import type { Subscription } from './subscription.interface';
+import type { SubscriptionOffer } from './subscription-offer.interface';
import type { SymbolMetrics } from './symbol-metrics.interface';
import type { SystemMessage } from './system-message.interface';
import type { TabConfiguration } from './tab-configuration.interface';
@@ -102,8 +102,8 @@ export {
ResponseError,
ScraperConfiguration,
Statistics,
+ SubscriptionOffer,
SystemMessage,
- Subscription,
SymbolMetrics,
TabConfiguration,
ToggleOption,
diff --git a/libs/common/src/lib/interfaces/info-item.interface.ts b/libs/common/src/lib/interfaces/info-item.interface.ts
index 1b3926331..bd3eb1f94 100644
--- a/libs/common/src/lib/interfaces/info-item.interface.ts
+++ b/libs/common/src/lib/interfaces/info-item.interface.ts
@@ -1,9 +1,9 @@
-import { SubscriptionOffer } from '@ghostfolio/common/types';
+import { SubscriptionOfferKey } from '@ghostfolio/common/types';
import { Platform, SymbolProfile } from '@prisma/client';
import { Statistics } from './statistics.interface';
-import { Subscription } from './subscription.interface';
+import { SubscriptionOffer } from './subscription-offer.interface';
export interface InfoItem {
baseCurrency: string;
@@ -18,5 +18,5 @@ export interface InfoItem {
platforms: Platform[];
statistics: Statistics;
stripePublicKey?: string;
- subscriptions: { [offer in SubscriptionOffer]: Subscription };
+ subscriptionOffers: { [offer in SubscriptionOfferKey]: SubscriptionOffer };
}
diff --git a/libs/common/src/lib/interfaces/subscription-offer.interface.ts b/libs/common/src/lib/interfaces/subscription-offer.interface.ts
new file mode 100644
index 000000000..8db91da6e
--- /dev/null
+++ b/libs/common/src/lib/interfaces/subscription-offer.interface.ts
@@ -0,0 +1,9 @@
+import { StringValue } from 'ms';
+
+export interface SubscriptionOffer {
+ coupon?: number;
+ couponId?: string;
+ durationExtension?: StringValue;
+ price: number;
+ priceId: string;
+}
diff --git a/libs/common/src/lib/interfaces/subscription.interface.ts b/libs/common/src/lib/interfaces/subscription.interface.ts
deleted file mode 100644
index 29f5d3aba..000000000
--- a/libs/common/src/lib/interfaces/subscription.interface.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export interface Subscription {
- coupon?: number;
- couponId?: string;
- price: number;
- priceId: string;
-}
diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts
index 27cd1a610..647822d34 100644
--- a/libs/common/src/lib/interfaces/user.interface.ts
+++ b/libs/common/src/lib/interfaces/user.interface.ts
@@ -1,4 +1,4 @@
-import { SubscriptionOffer } from '@ghostfolio/common/types';
+import { SubscriptionOfferKey } from '@ghostfolio/common/types';
import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type';
import { Account, Tag } from '@prisma/client';
@@ -20,7 +20,7 @@ export interface User {
systemMessage?: SystemMessage;
subscription: {
expiresAt?: Date;
- offer: SubscriptionOffer;
+ offer: SubscriptionOfferKey;
type: SubscriptionType;
};
tags: (Tag & { isUsed: boolean })[];
diff --git a/libs/common/src/lib/types/index.ts b/libs/common/src/lib/types/index.ts
index a66755ab1..9e8178d3c 100644
--- a/libs/common/src/lib/types/index.ts
+++ b/libs/common/src/lib/types/index.ts
@@ -15,7 +15,7 @@ import type { MarketState } from './market-state.type';
import type { Market } from './market.type';
import type { OrderWithAccount } from './order-with-account.type';
import type { RequestWithUser } from './request-with-user.type';
-import type { SubscriptionOffer } from './subscription-offer.type';
+import type { SubscriptionOfferKey } from './subscription-offer-key.type';
import type { UserWithSettings } from './user-with-settings.type';
import type { ViewMode } from './view-mode.type';
@@ -37,7 +37,7 @@ export type {
MarketState,
OrderWithAccount,
RequestWithUser,
- SubscriptionOffer,
+ SubscriptionOfferKey,
UserWithSettings,
ViewMode
};
diff --git a/libs/common/src/lib/types/subscription-offer.type.ts b/libs/common/src/lib/types/subscription-offer-key.type.ts
similarity index 71%
rename from libs/common/src/lib/types/subscription-offer.type.ts
rename to libs/common/src/lib/types/subscription-offer-key.type.ts
index 98977da45..f6d898a01 100644
--- a/libs/common/src/lib/types/subscription-offer.type.ts
+++ b/libs/common/src/lib/types/subscription-offer-key.type.ts
@@ -1,4 +1,4 @@
-export type SubscriptionOffer =
+export type SubscriptionOfferKey =
| 'default'
| 'renewal'
| 'renewal-early-bird-2023'
diff --git a/libs/common/src/lib/types/user-with-settings.type.ts b/libs/common/src/lib/types/user-with-settings.type.ts
index 59e9f142d..2a669d26f 100644
--- a/libs/common/src/lib/types/user-with-settings.type.ts
+++ b/libs/common/src/lib/types/user-with-settings.type.ts
@@ -1,5 +1,5 @@
import { UserSettings } from '@ghostfolio/common/interfaces';
-import { SubscriptionOffer } from '@ghostfolio/common/types';
+import { SubscriptionOfferKey } from '@ghostfolio/common/types';
import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type';
import { Access, Account, Settings, User } from '@prisma/client';
@@ -13,7 +13,7 @@ export type UserWithSettings = User & {
Settings: Settings & { settings: UserSettings };
subscription?: {
expiresAt?: Date;
- offer: SubscriptionOffer;
+ offer: SubscriptionOfferKey;
type: SubscriptionType;
};
};