From 251e194ab3ca7a541fbd934b287209618194ddad Mon Sep 17 00:00:00 2001 From: VitaliyHrabovych Date: Fri, 4 Jul 2025 16:15:42 +0300 Subject: [PATCH 1/7] Add tracking functionality for df-paywall component --- .../df-manage-scripts.component.html | 2 +- .../df-manage-limits.component.html | 2 +- .../df-manage-service-report.component.html | 2 +- .../df-manage-scheduler.component.html | 2 +- .../df-manage-services.component.html | 2 +- .../df-paywall/df-paywall.component.ts | 44 ++++++++++++++++++- .../shared/services/df-user-data.service.ts | 4 ++ 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html b/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html index 9da55379..e3defded 100644 --- a/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html +++ b/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/adf-limits/df-manage-limits/df-manage-limits.component.html b/src/app/adf-limits/df-manage-limits/df-manage-limits.component.html index f8c7bb58..30ac6dea 100644 --- a/src/app/adf-limits/df-manage-limits/df-manage-limits.component.html +++ b/src/app/adf-limits/df-manage-limits/df-manage-limits.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html b/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html index be87d3e1..0bf4c9ec 100644 --- a/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html +++ b/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html b/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html index dad2f333..150fe1a7 100644 --- a/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html +++ b/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/adf-services/df-manage-services/df-manage-services.component.html b/src/app/adf-services/df-manage-services/df-manage-services.component.html index 9da55379..d13f6a25 100644 --- a/src/app/adf-services/df-manage-services/df-manage-services.component.html +++ b/src/app/adf-services/df-manage-services/df-manage-services.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/shared/components/df-paywall/df-paywall.component.ts b/src/app/shared/components/df-paywall/df-paywall.component.ts index 4600511a..03710c18 100644 --- a/src/app/shared/components/df-paywall/df-paywall.component.ts +++ b/src/app/shared/components/df-paywall/df-paywall.component.ts @@ -1,5 +1,15 @@ -import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; +import { + AfterViewInit, + Component, + ElementRef, + ViewChild, + Input, + OnInit, +} from '@angular/core'; import { TranslocoPipe } from '@ngneat/transloco'; +import { DfUserDataService } from '../../services/df-user-data.service'; +import { DfSystemConfigDataService } from '../../services/df-system-config-data.service'; +import { HttpClient } from '@angular/common/http'; @Component({ selector: 'df-paywall', @@ -8,8 +18,19 @@ import { TranslocoPipe } from '@ngneat/transloco'; standalone: true, imports: [TranslocoPipe], }) -export class DfPaywallComponent implements AfterViewInit { +export class DfPaywallComponent implements AfterViewInit, OnInit { @ViewChild('calendlyWidget') calendlyWidget: ElementRef; + @Input() serviceName: string = 'Unknown'; + + constructor( + private userDataService: DfUserDataService, + private systemConfigService: DfSystemConfigDataService, + private http: HttpClient + ) {} + + ngOnInit(): void { + this.trackPaywallHit(); + } ngAfterViewInit(): void { (window as any)['Calendly'].initInlineWidget({ @@ -18,4 +39,23 @@ export class DfPaywallComponent implements AfterViewInit { autoLoad: false, }); } + + private trackPaywallHit(): void { + const user = this.userDataService.userData; + const email = user?.email ?? 'unknown'; + const ip = + this.systemConfigService?.environment?.client?.ipAddress ?? 'unknown'; + const service = this.serviceName || 'Not specified'; + + const payload = { email, ip_address: ip, service_name: service }; + + this.http + .post('https://updates.dreamfactory.com/api/paywall', payload) + .subscribe({ + next: () => {}, + error: err => { + console.error('Paywall tracking failed:', err); + }, + }); + } } diff --git a/src/app/shared/services/df-user-data.service.ts b/src/app/shared/services/df-user-data.service.ts index a6aba456..78ebb037 100644 --- a/src/app/shared/services/df-user-data.service.ts +++ b/src/app/shared/services/df-user-data.service.ts @@ -60,6 +60,10 @@ export class DfUserDataService { this.isLoggedIn = false; } + get userData(): UserSession | null { + return this.userDataSubject.value; + } + set userData(userData: UserSession | null) { this.userDataSubject.next(userData); if (userData) { From 7dfe84c38e2b394b07a21a1d91d682eb79804a51 Mon Sep 17 00:00:00 2001 From: VitaliyHrabovych Date: Fri, 4 Jul 2025 16:17:16 +0300 Subject: [PATCH 2/7] Add tracking functionality for df-paywall component --- .../df-manage-scripts/df-manage-scripts.component.html | 4 +++- .../df-manage-service-report.component.html | 4 +++- .../df-manage-scheduler/df-manage-scheduler.component.html | 4 +++- .../df-manage-services-table.component.ts | 6 +++--- .../df-manage-services/df-manage-services.component.html | 4 +++- src/environments/environment.ts | 2 +- 6 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html b/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html index e3defded..d6a6c1d8 100644 --- a/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html +++ b/src/app/adf-event-scripts/df-manage-scripts/df-manage-scripts.component.html @@ -1,4 +1,6 @@ - + diff --git a/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html b/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html index 0bf4c9ec..ce7fa7f2 100644 --- a/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html +++ b/src/app/adf-reports/df-manage-service-report/df-manage-service-report.component.html @@ -1,4 +1,6 @@ - + diff --git a/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html b/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html index 150fe1a7..a5cc55a6 100644 --- a/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html +++ b/src/app/adf-scheduler/df-manage-scheduler/df-manage-scheduler.component.html @@ -1,4 +1,6 @@ - + diff --git a/src/app/adf-services/df-manage-services/df-manage-services-table.component.ts b/src/app/adf-services/df-manage-services/df-manage-services-table.component.ts index b36f3fb5..e59be6bd 100644 --- a/src/app/adf-services/df-manage-services/df-manage-services-table.component.ts +++ b/src/app/adf-services/df-manage-services/df-manage-services-table.component.ts @@ -124,9 +124,9 @@ export class DfManageServicesTableComponent extends DfManageTableComponent src.name) - .join('","')}"))`; + filter = `${ + filter ? `(${filter}) and ` : '' + }(type in ("${this.serviceTypes.map(src => src.name).join('","')}"))`; } this.serviceService diff --git a/src/app/adf-services/df-manage-services/df-manage-services.component.html b/src/app/adf-services/df-manage-services/df-manage-services.component.html index d13f6a25..426e1ea0 100644 --- a/src/app/adf-services/df-manage-services/df-manage-services.component.html +++ b/src/app/adf-services/df-manage-services/df-manage-services.component.html @@ -1,4 +1,6 @@ - + diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 10933075..05bf35d0 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -2,5 +2,5 @@ export const environment = { production: false, dfAdminApiKey: '', dfApiDocsApiKey: '', - dfFileManagerApiKey: '' + dfFileManagerApiKey: '', }; From 8f989c8b3773724a2265cbe121bfbddb84c68a64 Mon Sep 17 00:00:00 2001 From: VitaliyHrabovych Date: Fri, 4 Jul 2025 16:17:30 +0300 Subject: [PATCH 3/7] Revert "Merge pull request #421 from dreamfactorysoftware/revert-420-ai-button" This reverts commit 4f921297668611f967fa62402969129b151e875e, reversing changes made to bae49b053d3d8be172b5712441ab2b4c6c203608. --- .gitignore | 2 + src/app/ai/ai.component.ts | 245 ++++++++++++++++++ src/app/routes.ts | 6 + .../df-side-nav/df-side-nav.component.html | 1 + .../df-side-nav/df-side-nav.component.scss | 120 +++++++++ src/app/shared/types/routes.ts | 1 + src/app/shared/utilities/route.ts | 3 +- src/assets/i18n/en.json | 4 + src/assets/img/nav/ai.svg | 14 + src/environments/environment.ts | 1 + 10 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 src/app/ai/ai.component.ts create mode 100644 src/assets/img/nav/ai.svg diff --git a/.gitignore b/.gitignore index 7a4a8630..58f8ab12 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ testem.log # System files .DS_Store Thumbs.db + +.config/ diff --git a/src/app/ai/ai.component.ts b/src/app/ai/ai.component.ts new file mode 100644 index 00000000..2c29a2fe --- /dev/null +++ b/src/app/ai/ai.component.ts @@ -0,0 +1,245 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ActivatedRoute } from '@angular/router'; +import { DfPaywallComponent } from '../shared/components/df-paywall/df-paywall.component'; +import { NgIf } from '@angular/common'; + +@Component({ + selector: 'app-ai', + standalone: true, + imports: [CommonModule, DfPaywallComponent, NgIf], + template: ` +
+
+
+

AI Gateway Data Platform

+
+

+ Unlock the power of AI with your data! Our upcoming AI + capabilities will enable you to: +

+
    +
  • + ✨ Secure Dataset Exposure: Safely expose your + datasets to AI clients with full RBAC protections +
  • +
  • + 🔐 Enterprise-Grade Security: Maintain complete + control over data access and permissions +
  • +
  • + 🚀 Seamless Integration: Connect popular AI + platforms and tools directly to your DreamFactory APIs +
  • +
  • + 📊 Intelligent Analytics: Generate insights and + recommendations powered by machine learning +
  • +
+
+

🎯 Ready to Get Started?

+

+ Contact us below to join our exclusive AI beta program and be + among the first to experience these cutting-edge capabilities! +

+
+
+
+
+ +
+ +
+

AI Assistant

+

+ Welcome to the AI section! This is where AI-powered features will be + implemented. +

+
+
+

Smart Analytics

+

AI-powered data insights and analytics

+
+
+

Automated Tasks

+

Intelligent automation and task management

+
+
+

Predictive Modeling

+

Advanced machine learning predictions

+
+
+
+
+ `, + styles: [ + ` + .ai-paywall-container { + min-height: 100vh; + background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + } + + .ai-intro-section { + padding: 3rem 2rem; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + position: relative; + overflow: hidden; + } + + .ai-intro-section::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,'); + opacity: 0.3; + } + + .ai-intro-content { + max-width: 800px; + margin: 0 auto; + position: relative; + z-index: 1; + } + + .ai-title { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 1.5rem; + text-align: center; + color: #000; + } + + @keyframes rainbow-text { + 0%, + 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + } + + .lead-text { + font-size: 1.2rem; + font-weight: 500; + margin-bottom: 2rem; + text-align: center; + opacity: 0.95; + } + + .feature-list { + list-style: none; + padding: 0; + margin: 2rem 0; + } + + .feature-list li { + padding: 0.8rem 0; + font-size: 1.1rem; + display: flex; + align-items: center; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + opacity: 0.9; + } + + .feature-list li:last-child { + border-bottom: none; + } + + .beta-callout { + background: rgba(255, 255, 255, 0.1); + padding: 1.5rem; + border-radius: 12px; + margin-top: 2rem; + text-align: center; + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + } + + .beta-callout h3 { + margin-bottom: 0.5rem; + font-size: 1.3rem; + color: #ffd700; + } + + .beta-callout p { + margin: 0; + font-size: 1rem; + opacity: 0.9; + } + + .ai-container { + padding: 2rem; + max-width: 1200px; + margin: 0 auto; + } + + h1 { + color: #333; + margin-bottom: 1rem; + background: linear-gradient( + 45deg, + #dc143c, + #ff4500, + #ffa500, + #32cd32, + #1e90ff, + #8a2be2 + ); + background-size: 300% 300%; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: rainbow-text 3s ease-in-out infinite; + font-weight: 700; + text-shadow: 0 0 15px rgba(0, 0, 0, 0.2); + filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.1)); + } + + .ai-content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin-top: 2rem; + } + + .feature-card { + background: #f8f9fa; + padding: 1.5rem; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: transform 0.2s ease; + } + + .feature-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + + .feature-card h3 { + margin-bottom: 0.5rem; + color: #333; + } + + .feature-card p { + color: #666; + line-height: 1.5; + } + `, + ], +}) +export class AiComponent { + paywall = false; + + constructor(private activatedRoute: ActivatedRoute) { + this.activatedRoute.data.subscribe(({ showPaywall }) => { + if (showPaywall) { + this.paywall = true; + } + }); + } +} diff --git a/src/app/routes.ts b/src/app/routes.ts index ae260047..ad9733f1 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -799,6 +799,12 @@ export const routes: Routes = [ ], canActivate: [loggedInGuard, licenseGuard], }, + { + path: ROUTES.AI, + loadComponent: () => import('./ai/ai.component').then(m => m.AiComponent), + canActivate: [loggedInGuard, licenseGuard], + data: { showPaywall: true }, + }, { path: ROUTES.PROFILE, loadComponent: () => diff --git a/src/app/shared/components/df-side-nav/df-side-nav.component.html b/src/app/shared/components/df-side-nav/df-side-nav.component.html index c8ded1a4..2a1e316e 100644 --- a/src/app/shared/components/df-side-nav/df-side-nav.component.html +++ b/src/app/shared/components/df-side-nav/df-side-nav.component.html @@ -154,6 +154,7 @@

class="nav-item" [class.active]="isActive(item)" [class.commercial-feature]="isFeatureLocked(item.path, licenseType)" + [class.ai-nav-item]="item.path === '/ai'" (click)="handleNavClick(item)"> diff --git a/src/app/shared/components/df-side-nav/df-side-nav.component.scss b/src/app/shared/components/df-side-nav/df-side-nav.component.scss index 3d3ebe4f..7ce4ff17 100644 --- a/src/app/shared/components/df-side-nav/df-side-nav.component.scss +++ b/src/app/shared/components/df-side-nav/df-side-nav.component.scss @@ -159,6 +159,126 @@ $red-palette: mat.define-palette(mat.$red-palette); ::ng-deep .mat-mdc-button-touch-target { background-color: #f6f2fa; } + + // Special styling for AI navigation item + &.ai-nav-item { + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(220, 20, 60, 0.15), + rgba(255, 69, 0, 0.15), + rgba(255, 165, 0, 0.15), + rgba(50, 205, 50, 0.15), + rgba(30, 144, 255, 0.15), + rgba(138, 43, 226, 0.15), + transparent + ); + animation: rainbow-slide 3s ease-in-out infinite; + } + + span { + background: linear-gradient( + 45deg, + #dc143c, + #ff4500, + #ffa500, + #32cd32, + #1e90ff, + #8a2be2 + ); + background-size: 300% 300%; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: rainbow-text 2s ease-in-out infinite; + font-weight: 700; + text-shadow: 0 0 15px rgba(0, 0, 0, 0.3); + filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.2)); + } + + img { + filter: hue-rotate(0deg) saturate(2) brightness(0.8) contrast(1.3); + animation: rainbow-icon 4s linear infinite; + drop-shadow: 0 0 6px rgba(0, 0, 0, 0.3); + } + + &:hover { + &::before { + animation-duration: 1s; + } + + span { + animation-duration: 1s; + } + + img { + animation-duration: 2s; + } + } + } + + @keyframes rainbow-slide { + 0% { + left: -100%; + } + 50% { + left: 100%; + } + 100% { + left: -100%; + } + } + + @keyframes rainbow-text { + 0%, + 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + } + + @keyframes rainbow-icon { + 0% { + filter: hue-rotate(0deg) saturate(2) brightness(0.8) contrast(1.3) + drop-shadow(0 0 6px rgba(220, 20, 60, 0.4)); + } + 16.66% { + filter: hue-rotate(60deg) saturate(2.2) brightness(0.7) contrast(1.4) + drop-shadow(0 0 6px rgba(255, 69, 0, 0.4)); + } + 33.33% { + filter: hue-rotate(120deg) saturate(2.4) brightness(0.6) contrast(1.5) + drop-shadow(0 0 6px rgba(255, 165, 0, 0.4)); + } + 50% { + filter: hue-rotate(180deg) saturate(2.2) brightness(0.7) contrast(1.4) + drop-shadow(0 0 6px rgba(50, 205, 50, 0.4)); + } + 66.66% { + filter: hue-rotate(240deg) saturate(2) brightness(0.8) contrast(1.3) + drop-shadow(0 0 6px rgba(30, 144, 255, 0.4)); + } + 83.33% { + filter: hue-rotate(300deg) saturate(2.2) brightness(0.7) contrast(1.4) + drop-shadow(0 0 6px rgba(138, 43, 226, 0.4)); + } + 100% { + filter: hue-rotate(360deg) saturate(2) brightness(0.8) contrast(1.3) + drop-shadow(0 0 6px rgba(220, 20, 60, 0.4)); + } + } &.active { ::ng-deep .mat-mdc-button-touch-target { background-color: #e3dfff; diff --git a/src/app/shared/types/routes.ts b/src/app/shared/types/routes.ts index 768263d9..3794b4b8 100644 --- a/src/app/shared/types/routes.ts +++ b/src/app/shared/types/routes.ts @@ -16,6 +16,7 @@ export enum ROUTES { QUICKSTART = 'quickstart', RESOURCES = 'resources', DOWNLOAD = 'download', + AI = 'ai', API_CONNECTIONS = 'api-connections', API_TYPES = 'api-types', DATABASE = 'database', diff --git a/src/app/shared/utilities/route.ts b/src/app/shared/utilities/route.ts index 3ea19119..5b04e1c1 100644 --- a/src/app/shared/utilities/route.ts +++ b/src/app/shared/utilities/route.ts @@ -15,6 +15,7 @@ const filteredFromNav = [ const navIcons = [ 'home', + 'ai', 'admin-settings', 'api-connections', 'api-security', @@ -62,7 +63,7 @@ export function accessibleRoutes( navs: Array