Skip to content

Commit a9fad72

Browse files
committed
add additional components and add new config values
1 parent ac3c14e commit a9fad72

21 files changed

+434
-14
lines changed

projects/fusio-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ngx-fusio-sdk",
3-
"version": "1.1.2",
3+
"version": "1.1.3",
44
"description": "SDK to integrate Fusio into an Angular app",
55
"keywords": [
66
"Fusio",

projects/fusio-sdk/src/lib/component/account-container/account-container.component.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, Input, OnInit} from '@angular/core';
1+
import {Component, OnInit} from '@angular/core';
22
import {ActivatedRoute, Router} from "@angular/router";
33

44
@Component({
@@ -8,12 +8,7 @@ import {ActivatedRoute, Router} from "@angular/router";
88
})
99
export class AccountContainerComponent implements OnInit {
1010

11-
@Input()
12-
options: Array<string> = ['account', 'security', 'app', 'event', 'subscription'];
13-
14-
@Input()
1511
active: string = 'account';
16-
1712
items: Array<Item> = this.getItems();
1813

1914
constructor(private router: Router, private route: ActivatedRoute) { }
@@ -30,8 +25,8 @@ export class AccountContainerComponent implements OnInit {
3025
this.items = this.getItems();
3126
}
3227

33-
protected getItems(): Array<Item> {
34-
const items = [{
28+
private getItems(): Array<Item> {
29+
return [{
3530
id: 'account',
3631
link: '/account',
3732
name: 'Account',
@@ -52,10 +47,6 @@ export class AccountContainerComponent implements OnInit {
5247
link: '/account/subscription',
5348
name: 'Subscriptions',
5449
}];
55-
56-
return items.filter((item) => {
57-
return this.options.includes(item.id);
58-
});
5950
}
6051

6152
}

projects/fusio-sdk/src/lib/component/backend-container/backend-container.component.css

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
<div class="row">
3+
<div class="col-2 text-bg-dark">
4+
<hr>
5+
<ul ngbNav [(activeId)]="active" class="nav nav-pills flex-column mb-auto">
6+
<li *ngFor="let item of items" [ngbNavItem]="item.id">
7+
<a ngbNavLink routerLink="/backend{{item.link}}" class="text-white">
8+
<i class="bi {{item.icon}}"></i>
9+
{{item.name}}
10+
</a>
11+
</li>
12+
</ul>
13+
</div>
14+
<div class="col-10">
15+
<router-outlet></router-outlet>
16+
</div>
17+
</div>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import {Item} from "../../config/config";
3+
import {ConfigService} from "../../service/config.service";
4+
import {ActivatedRoute, Router} from "@angular/router";
5+
6+
@Component({
7+
selector: 'fusio-backend-container',
8+
templateUrl: './backend-container.component.html',
9+
styleUrls: ['./backend-container.component.css']
10+
})
11+
export class BackendContainerComponent implements OnInit {
12+
13+
active?: string;
14+
items?: Array<Item>;
15+
16+
constructor(private config: ConfigService, private router: Router, private route: ActivatedRoute) { }
17+
18+
ngOnInit(): void {
19+
this.items = this.config.getBackendConfig()?.navigation;
20+
if (this.items) {
21+
this.active = this.items[0].id;
22+
}
23+
24+
this.route.url.subscribe(() => {
25+
this.items?.forEach((item) => {
26+
if (this.router.url.startsWith(item.link)) {
27+
this.active = item.id;
28+
}
29+
});
30+
})
31+
}
32+
33+
}

projects/fusio-sdk/src/lib/component/faq/faq.component.css

Whitespace-only changes.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
<div class="fusio-page">
3+
<div class="p-3 pb-md-4 mx-auto text-center">
4+
<h1 class="display-4 fw-normal">FAQ</h1>
5+
<p class="fs-5 text-muted">{{description}}</p>
6+
</div>
7+
<div class="p-3 pb-md-4 mx-auto">
8+
<ng-container *ngFor="let question of questions">
9+
<h4 class="display-7">{{question.title}}</h4>
10+
<p class="fs-5">{{question.answer}}</p>
11+
<hr>
12+
</ng-container>
13+
</div>
14+
</div>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {Component, OnInit} from '@angular/core';
2+
import {ConfigService} from "../../service/config.service";
3+
import {Question} from "../../config/config";
4+
5+
@Component({
6+
selector: 'fusio-faq',
7+
templateUrl: './faq.component.html',
8+
styleUrls: ['./faq.component.css']
9+
})
10+
export class FaqComponent implements OnInit {
11+
12+
description?: string;
13+
questions?: Array<Question>;
14+
15+
constructor(private config: ConfigService) { }
16+
17+
ngOnInit(): void {
18+
this.description = this.config.getFaqConfig()?.description;
19+
this.questions = this.config.getFaqConfig()?.questions;
20+
}
21+
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
.fusio-headline {
3+
margin-left:-1rem;
4+
margin-right:-1rem;
5+
margin-top:-1rem;
6+
border-top:2px solid #eee;
7+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
<div class="fusio-page">
3+
<div class="fusio-headline p-3 pb-md-4 text-center text-white" [ngStyle]="{background: 'url(assets/' + backgroundImage}">
4+
<br>
5+
<h1 class="display-4 fw-normal">{{headline}}</h1>
6+
<br>
7+
<p class="lead fw-normal">{{description}}</p>
8+
<br>
9+
</div>
10+
11+
<br>
12+
<h2 class="fs-2 text-center pt-4">Features</h2>
13+
<br>
14+
15+
<div class="row g-4 py-5 row-cols-1 row-cols-lg-3 text-center">
16+
<div class="feature col" *ngFor="let feature of features">
17+
<div class="d-inline-flex align-items-center justify-content-center text-bg-primary bg-gradient fs-2 mb-3 rounded-5">
18+
<i class="bi {{feature.icon}} p-4"></i>
19+
</div>
20+
<h3 class="fs-2">{{feature.title}}</h3>
21+
<p>{{feature.description}}</p>
22+
</div>
23+
</div>
24+
25+
<ng-container *ngIf="youtube">
26+
<br>
27+
<h2 class="fs-2 text-center pt-4">Introduction</h2>
28+
<br>
29+
30+
<div style="text-align: center">
31+
<iframe width="560" height="315" src="https://www.youtube.com/embed/{{youtube}}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
32+
</div>
33+
</ng-container>
34+
35+
</div>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {Component, OnInit} from '@angular/core';
2+
import {ConfigService} from "../../service/config.service";
3+
import {Feature} from "../../config/config";
4+
5+
@Component({
6+
selector: 'fusio-home',
7+
templateUrl: './home.component.html',
8+
styleUrls: ['./home.component.css']
9+
})
10+
export class HomeComponent implements OnInit {
11+
12+
headline?: string;
13+
description?: string;
14+
backgroundImage?: string;
15+
features?: Array<Feature>;
16+
youtube?: string;
17+
18+
constructor(private config: ConfigService) { }
19+
20+
ngOnInit(): void {
21+
this.headline = this.config.getHomeConfig()?.headline;
22+
this.description = this.config.getHomeConfig()?.description;
23+
this.backgroundImage = this.config.getHomeConfig()?.backgroundImage;
24+
this.features = this.config.getHomeConfig()?.features;
25+
this.youtube = this.config.getHomeConfig()?.youtube;
26+
}
27+
28+
}

projects/fusio-sdk/src/lib/component/navigation/navigation.component.css

Whitespace-only changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
<nav class="navbar navbar-dark navbar-expand-lg bg-dark">
3+
<div class="container">
4+
<a routerLink="/" class="navbar-brand"><img src="assets/{{logo}}"></a>
5+
<button class="navbar-toggler" type="button" (click)="isMenuCollapsed = !isMenuCollapsed">
6+
<span class="navbar-toggler-icon"></span>
7+
</button>
8+
<div [ngbCollapse]="isMenuCollapsed" class="collapse navbar-collapse">
9+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
10+
<li class="nav-item">
11+
<a class="nav-link" routerLink="/">Home</a>
12+
</li>
13+
<li class="nav-item">
14+
<a class="nav-link" routerLink="/pricing">Pricing</a>
15+
</li>
16+
<li class="nav-item">
17+
<a class="nav-link" routerLink="/faq">FAQ</a>
18+
</li>
19+
<li class="nav-item">
20+
<a class="nav-link" href="{{apiUrl}}">API</a>
21+
</li>
22+
</ul>
23+
<span class="navbar-text me-2" *ngIf="isAuthenticated && account?.name">
24+
Logged in as <a routerLink="/account">{{ account?.name }}</a>
25+
</span>
26+
<form class="d-flex">
27+
<a *ngIf="isAuthenticated" class="btn btn-primary ms-2" routerLink="/backend">Backend</a>
28+
<a *ngIf="isAuthenticated" class="btn btn-secondary ms-2" routerLink="/logout">Logout</a>
29+
<a *ngIf="!isAuthenticated" class="btn btn-primary ms-2" routerLink="/login">Login</a>
30+
<a *ngIf="!isAuthenticated" class="btn btn-secondary ms-2" routerLink="/register">Register</a>
31+
</form>
32+
</div>
33+
</div>
34+
</nav>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {Component, OnInit} from '@angular/core';
2+
import {ConsumerService} from "../../service/consumer.service";
3+
import {UserAccount} from "fusio-sdk/dist/src/generated/consumer/UserAccount";
4+
import {UserService} from "../../service/user.service";
5+
import {ConfigService} from "../../service/config.service";
6+
7+
@Component({
8+
selector: 'fusio-navigation',
9+
templateUrl: './navigation.component.html',
10+
styleUrls: ['./navigation.component.css']
11+
})
12+
export class NavigationComponent implements OnInit {
13+
14+
isAuthenticated = true;
15+
isMenuCollapsed = true;
16+
account?: UserAccount;
17+
apiUrl?: string;
18+
logo?: string;
19+
20+
constructor(private consumer: ConsumerService, private user: UserService, private config: ConfigService) { }
21+
22+
ngOnInit(): void {
23+
this.isAuthenticated = this.consumer.hasValidToken();
24+
this.account = this.user.get();
25+
this.apiUrl = this.consumer.getBaseUrl();
26+
this.logo = this.config.getLogo();
27+
}
28+
29+
}

projects/fusio-sdk/src/lib/component/pricing/pricing.component.css

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
<div class="fusio-page">
3+
<div class="pricing-header p-3 pb-md-4 mx-auto text-center">
4+
<h1 class="display-4 fw-normal">Pricing</h1>
5+
<p class="fs-5 text-muted">{{description}}</p>
6+
<div *ngIf="isAuthenticated" class="mt-4">
7+
<button class="btn btn-primary" (click)="doBillingPortal()">Billing Portal</button>
8+
</div>
9+
</div>
10+
<fusio-message [response]="response"></fusio-message>
11+
<div class="row row-cols-1 row-cols-lg-3 text-center m-5">
12+
<div class="col" *ngFor="let product of products">
13+
<div class="card mb-4 rounded-3 shadow-sm">
14+
<div class="card-header py-3">
15+
<h4 class="my-0 fw-normal">{{product.title}}</h4>
16+
</div>
17+
<div class="card-body">
18+
<h1 class="card-title pricing-card-title">{{product.price|currency:paymentCurrency:'symbol'}} <small class="text-muted fw-light">/mo</small></h1>
19+
<ul class="list-unstyled mt-3 mb-4">
20+
<li *ngFor="let feature of product.features">{{feature}}</li>
21+
</ul>
22+
<button *ngIf="!isAuthenticated" type="button" routerLink="/register" class="w-100 btn btn-lg btn-primary">Register</button>
23+
<button *ngIf="isAuthenticated" type="button" (click)="doPurchase(1)" class="w-100 btn btn-lg btn-primary">Purchase</button>
24+
</div>
25+
</div>
26+
</div>
27+
</div>
28+
</div>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {Component, OnInit} from '@angular/core';
2+
import {ConsumerService} from "../../service/consumer.service";
3+
import {Message} from "fusio-sdk/dist/src/generated/consumer/Message";
4+
import {EventService} from "../../service/event.service";
5+
import {LocationStrategy} from "@angular/common";
6+
import {ErrorService} from "../../service/error.service";
7+
import {ConfigService} from "../../service/config.service";
8+
import {Plan} from "fusio-sdk/dist/src/generated/consumer/Plan";
9+
import {Product} from "../../config/config";
10+
11+
@Component({
12+
selector: 'fusio-pricing',
13+
templateUrl: './pricing.component.html',
14+
styleUrls: ['./pricing.component.css']
15+
})
16+
export class PricingComponent implements OnInit {
17+
18+
isAuthenticated = true;
19+
paymentCurrency: string = 'EUR';
20+
description?: string;
21+
products?: Array<Product>;
22+
response?: Message;
23+
24+
constructor(private consumer: ConsumerService, private event: EventService, private location: LocationStrategy, private error: ErrorService, private config: ConfigService) { }
25+
26+
ngOnInit(): void {
27+
this.isAuthenticated = this.consumer.hasValidToken();
28+
this.paymentCurrency = this.config.getPaymentCurrency();
29+
this.description = this.config.getPricingConfig()?.description;
30+
this.products = this.config.getPricingConfig()?.products;
31+
}
32+
33+
async doBillingPortal() {
34+
try {
35+
const path = this.location.prepareExternalUrl(this.config.getHomePath());
36+
const redirectUrl = location.origin + path;
37+
38+
const resource = await this.consumer.getClient().getConsumerPaymentByProviderPortal(this.config.getPaymentProvider());
39+
const response = await resource.consumerActionPaymentPortal({
40+
returnUrl: redirectUrl
41+
});
42+
43+
if (response.data.redirectUrl) {
44+
location.href = response.data.redirectUrl;
45+
} else {
46+
throw new Error('You can only visit the billing portal once you have successfully purchased a subscription');
47+
}
48+
} catch (error) {
49+
this.response = this.error.convert(error)
50+
}
51+
}
52+
53+
async doPurchase(planId: number) {
54+
try {
55+
const plan = await this.getPlanById(planId);
56+
57+
const path = this.location.prepareExternalUrl('/account/subscription/callback/' + planId);
58+
const redirectUrl = location.origin + path;
59+
60+
const resource = await this.consumer.getClient().getConsumerPaymentByProviderCheckout(this.config.getPaymentProvider());
61+
const response = await resource.consumerActionPaymentCheckout({
62+
planId: plan.id,
63+
returnUrl: redirectUrl,
64+
});
65+
66+
if (response.data.approvalUrl) {
67+
this.event.dispatchCheckout(plan);
68+
69+
location.href = response.data.approvalUrl;
70+
}
71+
} catch (error) {
72+
this.response = this.error.convert(error)
73+
}
74+
}
75+
76+
private async getPlanById(planId: number): Promise<Plan> {
77+
const resource = await this.consumer.getClient().getConsumerPlanByPlanId('' + planId);
78+
const response = await resource.consumerActionPlanGet();
79+
return response.data;
80+
}
81+
82+
}

0 commit comments

Comments
 (0)