Browse Source
Merge branch 'main' into task/improve-sorting-to-be-case-insensitive-in-platform-and-tag-management-of-admin-control-panel
pull/7061/head
Thomas Kaul
4 weeks ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with
71 additions and
26 deletions
-
CHANGELOG.md
-
apps/api/src/app/portfolio/portfolio.service.ts
-
apps/api/src/app/user/user.service.ts
-
apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts
-
apps/client/src/app/components/admin-users/admin-users.html
-
apps/client/src/app/pages/pricing/pricing-page.component.ts
-
apps/client/src/app/pages/pricing/pricing-page.html
-
libs/common/src/lib/config.ts
-
libs/common/src/lib/interfaces/index.ts
-
libs/common/src/lib/interfaces/referral-partner.interface.ts
-
libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts
-
libs/common/src/lib/interfaces/user.interface.ts
-
package-lock.json
-
package.json
|
|
|
@ -9,12 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
- Improved the sorting to be case-insensitive in the platform management of the admin control panel |
|
|
|
- Improved the sorting to be case-insensitive in the tag management of the admin control panel |
|
|
|
|
|
|
|
## 3.12.0 - 2026-06-17 |
|
|
|
|
|
|
|
### Changed |
|
|
|
|
|
|
|
- Improved the styling of the checkboxes to consistently use the primary color in their states |
|
|
|
- Improved the account name display in the accounts table |
|
|
|
- Improved the name display in the activities table |
|
|
|
- Improved the last activity display in the users table of the admin control panel |
|
|
|
- Improved the registration display in the users table of the admin control panel |
|
|
|
- Improved the user id display in the users table of the admin control panel |
|
|
|
- Improved the sorting to be case-insensitive in the platform management of the admin control panel |
|
|
|
- Improved the sorting to be case-insensitive in the tag management of the admin control panel |
|
|
|
- Deprecated `SymbolProfile` in favor of `assetProfile` in the endpoint `GET api/v1/portfolio/holding/:dataSource/:symbol` |
|
|
|
- Improved the language localization for German (`de`) |
|
|
|
- Upgraded `svgmap` from version `2.19.3` to `2.21.0` |
|
|
|
|
|
|
|
|
|
|
|
@ -957,6 +957,18 @@ export class PortfolioService { |
|
|
|
marketPriceMin, |
|
|
|
SymbolProfile, |
|
|
|
tags, |
|
|
|
assetProfile: { |
|
|
|
assetClass: SymbolProfile.assetClass, |
|
|
|
assetSubClass: SymbolProfile.assetSubClass, |
|
|
|
countries: SymbolProfile.countries, |
|
|
|
currency: SymbolProfile.currency, |
|
|
|
dataSource: SymbolProfile.dataSource, |
|
|
|
isin: SymbolProfile.isin, |
|
|
|
name: SymbolProfile.name, |
|
|
|
sectors: SymbolProfile.sectors, |
|
|
|
symbol: SymbolProfile.symbol, |
|
|
|
userId: SymbolProfile.userId |
|
|
|
}, |
|
|
|
averagePrice: averagePrice.toNumber(), |
|
|
|
dataProviderInfo: portfolioCalculator.getDataProviderInfos()?.[0], |
|
|
|
dividendInBaseCurrency: dividendInBaseCurrency.toNumber(), |
|
|
|
|
|
|
|
@ -29,6 +29,7 @@ import { |
|
|
|
DEFAULT_DATE_RANGE, |
|
|
|
DEFAULT_LANGUAGE_CODE, |
|
|
|
PROPERTY_IS_READ_ONLY_MODE, |
|
|
|
PROPERTY_REFERRAL_PARTNERS, |
|
|
|
PROPERTY_SYSTEM_MESSAGE, |
|
|
|
TAG_ID_EXCLUDE_FROM_ANALYSIS, |
|
|
|
locale as defaultLocale |
|
|
|
@ -36,6 +37,7 @@ import { |
|
|
|
import { SubscriptionType } from '@ghostfolio/common/enums'; |
|
|
|
import { |
|
|
|
User as IUser, |
|
|
|
ReferralPartner, |
|
|
|
SystemMessage, |
|
|
|
UserSettings |
|
|
|
} from '@ghostfolio/common/interfaces'; |
|
|
|
@ -153,6 +155,17 @@ export class UserService { |
|
|
|
(impersonationUserSettings?.settings as UserSettings)?.baseCurrency ?? |
|
|
|
(settings.settings as UserSettings)?.baseCurrency; |
|
|
|
|
|
|
|
let referralPartners: ReferralPartner[]; |
|
|
|
|
|
|
|
if ( |
|
|
|
this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') && |
|
|
|
subscription.type === SubscriptionType.Basic |
|
|
|
) { |
|
|
|
referralPartners = await this.propertyService.getByKey<ReferralPartner[]>( |
|
|
|
PROPERTY_REFERRAL_PARTNERS |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
let systemMessage: SystemMessage; |
|
|
|
|
|
|
|
const systemMessageProperty = |
|
|
|
@ -179,6 +192,7 @@ export class UserService { |
|
|
|
activitiesCount, |
|
|
|
id, |
|
|
|
permissions, |
|
|
|
referralPartners, |
|
|
|
subscription, |
|
|
|
systemMessage, |
|
|
|
tags, |
|
|
|
|
|
|
|
@ -64,6 +64,7 @@ export class TransformDataSourceInResponseInterceptor< |
|
|
|
paths: [ |
|
|
|
'activities[*].dataSource', |
|
|
|
'activities[*].SymbolProfile.dataSource', |
|
|
|
'assetProfile.dataSource', |
|
|
|
'benchmarks[*].dataSource', |
|
|
|
'errors[*].dataSource', |
|
|
|
'fearAndGreedIndex.CRYPTOCURRENCIES.dataSource', |
|
|
|
|
|
|
|
@ -77,7 +77,7 @@ |
|
|
|
</th> |
|
|
|
<td |
|
|
|
*matCellDef="let element" |
|
|
|
class="mat-mdc-cell px-1 py-2" |
|
|
|
class="mat-mdc-cell px-1 py-2 text-nowrap" |
|
|
|
mat-cell |
|
|
|
> |
|
|
|
{{ formatDistanceToNow(element.createdAt) }} |
|
|
|
@ -183,7 +183,7 @@ |
|
|
|
</th> |
|
|
|
<td |
|
|
|
*matCellDef="let element" |
|
|
|
class="mat-mdc-cell px-1 py-2" |
|
|
|
class="mat-mdc-cell px-1 py-2 text-nowrap" |
|
|
|
mat-cell |
|
|
|
> |
|
|
|
{{ formatDistanceToNow(element.lastActivity) }} |
|
|
|
|
|
|
|
@ -68,20 +68,6 @@ export class GfPricingPageComponent implements OnInit { |
|
|
|
'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM' |
|
|
|
); |
|
|
|
|
|
|
|
protected readonly referralBrokers = [ |
|
|
|
'Alpian', |
|
|
|
'DEGIRO', |
|
|
|
'finpension', |
|
|
|
'frankly', |
|
|
|
'Interactive Brokers', |
|
|
|
'Mintos', |
|
|
|
'Monefit SmartSaver', |
|
|
|
'Revolut', |
|
|
|
'Swissquote', |
|
|
|
'VIAC', |
|
|
|
'Zak' |
|
|
|
] as const; |
|
|
|
|
|
|
|
protected readonly routerLinkFeatures = publicRoutes.features.routerLink; |
|
|
|
protected readonly routerLinkRegister = publicRoutes.register.routerLink; |
|
|
|
protected user: User; |
|
|
|
|
|
|
|
@ -305,23 +305,23 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@if (user?.subscription?.type === 'Basic') { |
|
|
|
@if (user?.referralPartners?.length) { |
|
|
|
<div class="row"> |
|
|
|
<div class="col mt-3"> |
|
|
|
<p> |
|
|
|
<ng-container i18n>If you plan to open an account at</ng-container> |
|
|
|
<ng-container> </ng-container> |
|
|
|
@for ( |
|
|
|
broker of referralBrokers; |
|
|
|
track broker; |
|
|
|
partner of user.referralPartners; |
|
|
|
track partner.name; |
|
|
|
let i = $index; |
|
|
|
let last = $last |
|
|
|
) { |
|
|
|
<i>{{ broker }}</i> |
|
|
|
<i>{{ partner.name }}</i> |
|
|
|
@if (last) { |
|
|
|
<span>, </span> |
|
|
|
} @else { |
|
|
|
@if (i === referralBrokers.length - 2) { |
|
|
|
@if (i === user.referralPartners.length - 2) { |
|
|
|
<ng-container> </ng-container> |
|
|
|
<ng-container i18n>or</ng-container> |
|
|
|
<ng-container> </ng-container> |
|
|
|
|
|
|
|
@ -253,6 +253,7 @@ export const PROPERTY_IS_READ_ONLY_MODE = 'IS_READ_ONLY_MODE'; |
|
|
|
export const PROPERTY_IS_USER_SIGNUP_ENABLED = 'IS_USER_SIGNUP_ENABLED'; |
|
|
|
export const PROPERTY_OPENROUTER_MODEL = 'OPENROUTER_MODEL'; |
|
|
|
export const PROPERTY_OPENROUTER_MODEL_WEB_FETCH = 'OPENROUTER_MODEL_WEB_FETCH'; |
|
|
|
export const PROPERTY_REFERRAL_PARTNERS = 'REFERRAL_PARTNERS'; |
|
|
|
export const PROPERTY_SLACK_COMMUNITY_USERS = 'SLACK_COMMUNITY_USERS'; |
|
|
|
export const PROPERTY_STRIPE_CONFIG = 'STRIPE_CONFIG'; |
|
|
|
export const PROPERTY_SYSTEM_MESSAGE = 'SYSTEM_MESSAGE'; |
|
|
|
|
|
|
|
@ -38,6 +38,7 @@ import type { PortfolioPosition } from './portfolio-position.interface'; |
|
|
|
import type { PortfolioReportRule } from './portfolio-report-rule.interface'; |
|
|
|
import type { PortfolioSummary } from './portfolio-summary.interface'; |
|
|
|
import type { Product } from './product'; |
|
|
|
import type { ReferralPartner } from './referral-partner.interface'; |
|
|
|
import type { AccessTokenResponse } from './responses/access-token-response.interface'; |
|
|
|
import type { AccountBalancesResponse } from './responses/account-balances-response.interface'; |
|
|
|
import type { AccountResponse } from './responses/account-response.interface'; |
|
|
|
@ -181,6 +182,7 @@ export { |
|
|
|
PublicKeyCredentialRequestOptionsJSON, |
|
|
|
PublicPortfolioResponse, |
|
|
|
QuotesResponse, |
|
|
|
ReferralPartner, |
|
|
|
ResponseError, |
|
|
|
RuleSettings, |
|
|
|
ScraperConfiguration, |
|
|
|
|
|
|
|
@ -0,0 +1,3 @@ |
|
|
|
export interface ReferralPartner { |
|
|
|
name: string; |
|
|
|
} |
|
|
|
@ -9,6 +9,19 @@ import { Tag } from '@prisma/client'; |
|
|
|
|
|
|
|
export interface PortfolioHoldingResponse { |
|
|
|
activitiesCount: number; |
|
|
|
assetProfile: Pick< |
|
|
|
EnhancedSymbolProfile, |
|
|
|
| 'assetClass' |
|
|
|
| 'assetSubClass' |
|
|
|
| 'countries' |
|
|
|
| 'currency' |
|
|
|
| 'dataSource' |
|
|
|
| 'isin' |
|
|
|
| 'name' |
|
|
|
| 'sectors' |
|
|
|
| 'symbol' |
|
|
|
| 'userId' |
|
|
|
>; |
|
|
|
averagePrice: number; |
|
|
|
dataProviderInfo: DataProviderInfo; |
|
|
|
dateOfFirstActivity: string; |
|
|
|
@ -31,7 +44,10 @@ export interface PortfolioHoldingResponse { |
|
|
|
netPerformanceWithCurrencyEffect: number; |
|
|
|
performances: Benchmark['performances']; |
|
|
|
quantity: number; |
|
|
|
|
|
|
|
/* @deprecated */ |
|
|
|
SymbolProfile: EnhancedSymbolProfile; |
|
|
|
|
|
|
|
tags: Tag[]; |
|
|
|
value: number; |
|
|
|
} |
|
|
|
|
|
|
|
@ -3,6 +3,7 @@ import { AccountWithPlatform } from '@ghostfolio/common/types'; |
|
|
|
|
|
|
|
import { Access, Tag } from '@prisma/client'; |
|
|
|
|
|
|
|
import { ReferralPartner } from './referral-partner.interface'; |
|
|
|
import { SubscriptionOffer } from './subscription-offer.interface'; |
|
|
|
import { SystemMessage } from './system-message.interface'; |
|
|
|
import { UserSettings } from './user-settings.interface'; |
|
|
|
@ -15,6 +16,7 @@ export interface User { |
|
|
|
dateOfFirstActivity: Date; |
|
|
|
id: string; |
|
|
|
permissions: string[]; |
|
|
|
referralPartners?: ReferralPartner[]; |
|
|
|
settings: UserSettings; |
|
|
|
systemMessage?: SystemMessage; |
|
|
|
subscription: { |
|
|
|
|
|
|
|
@ -1,12 +1,12 @@ |
|
|
|
{ |
|
|
|
"name": "ghostfolio", |
|
|
|
"version": "3.11.0", |
|
|
|
"version": "3.12.0", |
|
|
|
"lockfileVersion": 3, |
|
|
|
"requires": true, |
|
|
|
"packages": { |
|
|
|
"": { |
|
|
|
"name": "ghostfolio", |
|
|
|
"version": "3.11.0", |
|
|
|
"version": "3.12.0", |
|
|
|
"hasInstallScript": true, |
|
|
|
"license": "AGPL-3.0", |
|
|
|
"dependencies": { |
|
|
|
|
|
|
|
@ -1,6 +1,6 @@ |
|
|
|
{ |
|
|
|
"name": "ghostfolio", |
|
|
|
"version": "3.11.0", |
|
|
|
"version": "3.12.0", |
|
|
|
"homepage": "https://ghostfol.io", |
|
|
|
"license": "AGPL-3.0", |
|
|
|
"repository": "https://github.com/ghostfolio/ghostfolio", |
|
|
|
|