From 792d7f174ffde41c78b3ad01b95171919bbc800b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 6 Dec 2025 08:47:28 +0100 Subject: [PATCH 1/4] Task/restructure pricing page (#6037) * Restructure pricing page --- .../src/app/pages/pricing/pricing-page.component.ts | 8 ++++++++ 1 file changed, 8 insertions(+) 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 3d8170b06..88958ea0d 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.component.ts +++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts @@ -54,23 +54,30 @@ export class GfPricingPageComponent implements OnDestroy, OnInit { public durationExtension: StringValue; public hasPermissionToCreateUser: boolean; public hasPermissionToUpdateUserSettings: boolean; + public importAndExportTooltipBasic = translate( 'DATA_IMPORT_AND_EXPORT_TOOLTIP_BASIC' ); + public importAndExportTooltipOSS = translate( 'DATA_IMPORT_AND_EXPORT_TOOLTIP_OSS' ); + public importAndExportTooltipPremium = translate( 'DATA_IMPORT_AND_EXPORT_TOOLTIP_PREMIUM' ); + public isLoggedIn: boolean; public label: string; public price: number; public priceId: string; + public professionalDataProviderTooltipPremium = translate( 'PROFESSIONAL_DATA_PROVIDER_TOOLTIP_PREMIUM' ); + public referralBrokers = [ + 'Alpian', 'DEGIRO', 'finpension', 'frankly', @@ -80,6 +87,7 @@ export class GfPricingPageComponent implements OnDestroy, OnInit { 'VIAC', 'Zak' ]; + public routerLinkFeatures = publicRoutes.features.routerLink; public routerLinkRegister = publicRoutes.register.routerLink; public user: User; From bcca6089966f76f807a4b21bccebeff660d38c94 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 6 Dec 2025 09:11:48 +0100 Subject: [PATCH 2/4] Task/increase numerical precision for cryptocurrency quantities in holding detail dialog (#6038) * Increase numerical precision for cryptocurrency quantities * Update changelog --- CHANGELOG.md | 1 + .../holding-detail-dialog.component.ts | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2e9f9823..82e43c554 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Changed +- Increased the numerical precision for cryptocurrency quantities in the holding detail dialog - Upgraded `envalid` from version `8.1.0` to `8.1.1` - Upgraded `prettier` from version `3.7.3` to `3.7.4` diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index 55574d202..6a7129fec 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -411,10 +411,10 @@ export class GfHoldingDetailDialogComponent implements OnDestroy, OnInit { if (Number.isInteger(this.quantity)) { this.quantityPrecision = 0; } else if (SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') { - if (this.quantity < 1) { - this.quantityPrecision = 7; + if (this.quantity < 10) { + this.quantityPrecision = 8; } else if (this.quantity < 1000) { - this.quantityPrecision = 5; + this.quantityPrecision = 6; } else if (this.quantity >= 10000000) { this.quantityPrecision = 0; } From 9aa73f74f8cf745b35f37ab1e92ce6657e4a355d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 6 Dec 2025 09:48:50 +0100 Subject: [PATCH 3/4] Feature/data source transformation in import for self-hosted environments (#6032) * Introduce data source transformation support for self-hosted environments * Update changelog --- CHANGELOG.md | 4 ++++ .../transform-data-source-in-request.interceptor.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82e43c554..e95b5509b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +#### Added + +- Introduced data source transformation support in the import functionality for self-hosted environments + #### Changed - Increased the numerical precision for cryptocurrency quantities in the holding detail dialog diff --git a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts index 3bd9c05e7..17c5ebe57 100644 --- a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts @@ -69,6 +69,19 @@ export class TransformDataSourceInRequestInterceptor< }); } } + } else { + if (request.body?.activities) { + request.body.activities = request.body.activities.map((activity) => { + if (DataSource[activity.dataSource]) { + return activity; + } else { + return { + ...activity, + dataSource: decodeDataSource(activity.dataSource) + }; + } + }); + } } return next.handle(); From bca5ce3f0414a3dfd6641756086d084e69649020 Mon Sep 17 00:00:00 2001 From: David Requeno <108202767+DavidReque@users.noreply.github.com> Date: Sat, 6 Dec 2025 03:10:38 -0600 Subject: [PATCH 4/4] Feature/add 3D hover effect to membership card component (#5966) * Add 3D hover effect to membership card component * Update changelog --- CHANGELOG.md | 1 + .../membership-card.component.html | 94 +++---- .../membership-card.component.scss | 244 +++++++++++++++--- .../membership-card.component.stories.ts | 5 + .../membership-card.component.ts | 1 + 5 files changed, 258 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e95b5509b..878d90326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 #### Added - Introduced data source transformation support in the import functionality for self-hosted environments +- Added an optional 3D hover effect to the membership card component #### Changed diff --git a/libs/ui/src/lib/membership-card/membership-card.component.html b/libs/ui/src/lib/membership-card/membership-card.component.html index 1c68f5e3f..9faac0d3d 100644 --- a/libs/ui/src/lib/membership-card/membership-card.component.html +++ b/libs/ui/src/lib/membership-card/membership-card.component.html @@ -1,50 +1,54 @@ -
- -
- -
- @if (hasPermissionToCreateApiKey) { -
-
API Key
-
-
* * * * * * * * *
-
- -
-
-
- } -
-
-
Membership
-
{{ name }}
+
+ diff --git a/libs/ui/src/lib/membership-card/membership-card.component.scss b/libs/ui/src/lib/membership-card/membership-card.component.scss index 270adc0f1..fcd923f12 100644 --- a/libs/ui/src/lib/membership-card/membership-card.component.scss +++ b/libs/ui/src/lib/membership-card/membership-card.component.scss @@ -1,71 +1,231 @@ :host { --borderRadius: 1rem; --borderWidth: 2px; + --hover3dSpotlightOpacity: 0.2; display: block; max-width: 25rem; padding-top: calc(1 * var(--borderWidth)); width: 100%; - .card-container { - border-radius: var(--borderRadius); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); + .card-wrapper { + &.hover-3d { + perspective: 1000px; + } - &:after { - animation: animatedborder 7s ease alternate infinite; - background: linear-gradient(60deg, #5073b8, #1098ad, #07b39b, #6fba82); - background-size: 300% 300%; + .card-container { border-radius: var(--borderRadius); - content: ''; - height: calc(100% + var(--borderWidth) * 2); - left: calc(-1 * var(--borderWidth)); - top: calc(-1 * var(--borderWidth)); - position: absolute; - width: calc(100% + var(--borderWidth) * 2); - z-index: -1; - - @keyframes animatedborder { - 0% { - background-position: 0% 50%; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); + + .card-item { + aspect-ratio: 1.586; + background-color: #1d2124; + border-radius: calc(var(--borderRadius) - var(--borderWidth)); + color: rgba(var(--light-primary-text)); + line-height: 1.2; + + button { + color: rgba(var(--light-primary-text)); + height: 1.5rem; + z-index: 3; } - 50% { - background-position: 100% 50%; + + .heading { + font-size: 13px; } - 100% { - background-position: 0% 50%; + + .value { + font-size: 18px; + } + } + + &:not(.premium) { + &::after { + opacity: 0; + } + + .card-item { + background-color: #ffffff; + color: rgba(var(--dark-primary-text)); } } } - .card-item { - aspect-ratio: 1.586; - background-color: #1d2124; - border-radius: calc(var(--borderRadius) - var(--borderWidth)); - color: rgba(var(--light-primary-text)); - line-height: 1.2; + &.hover-3d { + --hover3d-rotate-x: 0; + --hover3d-rotate-y: 0; + --hover3d-shine: 100% 100%; - button { - color: rgba(var(--light-primary-text)); - height: 1.5rem; + .card-container { + overflow: hidden; + position: relative; + scale: 1; + transform: rotate3d( + var(--hover3d-rotate-x), + var(--hover3d-rotate-y), + 0, + 10deg + ); + transform-style: preserve-3d; + transition: + box-shadow 400ms ease-out, + scale 500ms ease-out, + transform 500ms ease-out; + will-change: transform, scale; + + &::before { + background-image: radial-gradient( + circle at 50%, + rgba(255, 255, 255, var(--hover3dSpotlightOpacity)) 10%, + transparent 50% + ); + content: ''; + filter: blur(0.75rem); + height: 33.333%; + opacity: 0; + pointer-events: none; + position: absolute; + scale: 500%; + translate: var(--hover3d-shine); + transition: + opacity 400ms ease-out, + translate 400ms ease-out; + width: 33.333%; + z-index: 1; + } + + .card-item { + position: relative; + + .hover-zone { + height: 33.333%; + width: 33.333%; + z-index: 2; + + &:nth-child(1) { + left: 0; + top: 0; + } + + &:nth-child(2) { + left: 33.333%; + top: 0; + } + + &:nth-child(3) { + right: 0; + top: 0; + } + + &:nth-child(4) { + left: 0; + top: 33.333%; + } + + &:nth-child(5) { + left: 33.333%; + top: 33.333%; + } + + &:nth-child(6) { + right: 0; + top: 33.333%; + } + + &:nth-child(7) { + bottom: 0; + left: 0; + } + + &:nth-child(8) { + bottom: 0; + left: 33.333%; + } + + &:nth-child(9) { + bottom: 0; + right: 0; + } + } + } } - .heading { - font-size: 13px; + &:has(.hover-zone:hover) .card-container { + box-shadow: 0 18px 40px rgba(15, 23, 42, 0.3); + scale: 1.05; + + &::before { + opacity: 1; + } } - .value { - font-size: 18px; + &:has(.hover-zone:nth-child(1):hover) { + --hover3d-rotate-x: 1; + --hover3d-rotate-y: -1; + --hover3d-shine: 0% 0%; } - } - &:not(.premium) { - &:after { - opacity: 0; + &:has(.hover-zone:nth-child(2):hover) { + --hover3d-rotate-x: 1; + --hover3d-rotate-y: 0; + --hover3d-shine: 100% 0%; } - .card-item { - background-color: #ffffff; - color: rgba(var(--dark-primary-text)); + &:has(.hover-zone:nth-child(3):hover) { + --hover3d-rotate-x: 1; + --hover3d-rotate-y: 1; + --hover3d-shine: 200% 0%; + } + + &:has(.hover-zone:nth-child(4):hover) { + --hover3d-rotate-x: 0; + --hover3d-rotate-y: -1; + --hover3d-shine: 0% 100%; + } + + &:has(.hover-zone:nth-child(5):hover) { + --hover3d-rotate-x: 0; + --hover3d-rotate-y: 0; + --hover3d-shine: 100% 100%; + } + + &:has(.hover-zone:nth-child(6):hover) { + --hover3d-rotate-x: 0; + --hover3d-rotate-y: 1; + --hover3d-shine: 200% 100%; + } + + &:has(.hover-zone:nth-child(7):hover) { + --hover3d-rotate-x: -1; + --hover3d-rotate-y: -1; + --hover3d-shine: 0% 200%; + } + + &:has(.hover-zone:nth-child(8):hover) { + --hover3d-rotate-x: -1; + --hover3d-rotate-y: 0; + --hover3d-shine: 100% 200%; + } + + &:has(.hover-zone:nth-child(9):hover) { + --hover3d-rotate-x: -1; + --hover3d-rotate-y: 1; + --hover3d-shine: 200% 200%; + } + } + } + + @media (prefers-reduced-motion: reduce) { + .card-wrapper.hover-3d { + .card-container { + scale: 1 !important; + transform: none !important; + transition: none !important; + + &::before { + opacity: 0 !important; + transition: none !important; + } } } } diff --git a/libs/ui/src/lib/membership-card/membership-card.component.stories.ts b/libs/ui/src/lib/membership-card/membership-card.component.stories.ts index 6b6fbe038..0d475bda7 100644 --- a/libs/ui/src/lib/membership-card/membership-card.component.stories.ts +++ b/libs/ui/src/lib/membership-card/membership-card.component.stories.ts @@ -26,6 +26,9 @@ export default { }) ], argTypes: { + hover3d: { + control: { type: 'boolean' } + }, name: { control: { type: 'select' }, options: ['Basic', 'Premium'] @@ -37,6 +40,7 @@ type Story = StoryObj; export const Basic: Story = { args: { + hover3d: false, name: 'Basic' } }; @@ -45,6 +49,7 @@ export const Premium: Story = { args: { expiresAt: addYears(new Date(), 1).toLocaleDateString(), hasPermissionToCreateApiKey: true, + hover3d: false, name: 'Premium' } }; diff --git a/libs/ui/src/lib/membership-card/membership-card.component.ts b/libs/ui/src/lib/membership-card/membership-card.component.ts index 175a94f42..be223758d 100644 --- a/libs/ui/src/lib/membership-card/membership-card.component.ts +++ b/libs/ui/src/lib/membership-card/membership-card.component.ts @@ -34,6 +34,7 @@ import { GfLogoComponent } from '../logo'; export class GfMembershipCardComponent { @Input() public expiresAt: string; @Input() public hasPermissionToCreateApiKey: boolean; + @Input() public hover3d = false; @Input() public name: string; @Output() generateApiKeyClicked = new EventEmitter();