Skip to content

Commit ef8dafa

Browse files
Merge pull request #11 from hallisonbrancalhao/feature/dashboard
Feature/dashboard
2 parents ff904cf + c347b07 commit ef8dafa

File tree

71 files changed

+802
-51
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+802
-51
lines changed

Diff for: apps/bytebank/public/assets/icons/avatar.svg

+5

Diff for: apps/bytebank/public/assets/icons/close_icon.svg

+3

Diff for: apps/bytebank/public/assets/icons/eye.svg

+3

Diff for: apps/bytebank/public/assets/icons/icon_menu.svg

+3
792 Bytes
729 Bytes
660 Bytes

Diff for: apps/bytebank/src/app/app.config.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import { ApplicationConfig, inject, provideZoneChangeDetection } from '@angular/core';
22
import { provideRouter } from '@angular/router';
33
import { appRoutes } from './app.routes';
4-
import { provideHttpClient } from '@angular/common/http';
4+
import { provideHttpClient, withInterceptors } from '@angular/common/http';
55
import { provideApollo } from 'apollo-angular';
66
import { HttpLink } from 'apollo-angular/http';
77
import { InMemoryCache } from '@apollo/client';
88
import { environment } from '../environments/environment.dev';
9+
import { loggingInterceptor } from '../interceptors/http.interceptos';
910

1011
export const appConfig: ApplicationConfig = {
1112
providers: [
1213
provideApollo(() => {
1314
const httpLink = inject(HttpLink);
1415
return {
15-
link: httpLink.create({ uri: environment.apiUrl }),
16+
link: httpLink.create({ uri: `${environment.apiUrl}/graphql` }),
1617
cache: new InMemoryCache(),
1718
};
1819
}),
19-
provideHttpClient(),
20+
provideHttpClient(withInterceptors([loggingInterceptor])),
2021
provideZoneChangeDetection({ eventCoalescing: true }),
2122
provideRouter(appRoutes),
2223
],

Diff for: apps/bytebank/src/environments/environment.dev.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const environment = {
22
env: 'dev',
33
production: false,
4-
apiUrl: 'http://localhost:3000/graphql',
4+
apiUrl: 'http://localhost:3000',
55
};

Diff for: apps/bytebank/src/environments/environment.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const environment = {
22
env: 'prod',
33
production: true,
4-
apiUrl: 'https://fiap-tech-challenge-api-production.up.railway.app/graphql',
4+
apiUrl: 'https://fiap-tech-challenge-api-production.up.railway.app',
55
};

Diff for: apps/bytebank/src/interceptors/http.interceptos.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { inject, } from '@angular/core';
2+
import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';
3+
import { Observable } from 'rxjs';
4+
import { environment } from '../environments/environment.dev';
5+
import { AuthFacade } from '@fiap-tech-challenge/shared-data-access';
6+
7+
export function loggingInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
8+
const baseUrl = environment.apiUrl;
9+
const authToken = inject(AuthFacade).token$();
10+
const newReq = req.clone({
11+
url: `${baseUrl}${req.url}`,
12+
headers: req.headers.append('Authorization', `Bearer ${authToken}`),
13+
});
14+
return next(newReq);
15+
}

Diff for: apps/bytebank/tailwind.config.js

+26-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,32 @@ module.exports = {
88
...createGlobPatternsForDependencies(__dirname),
99
],
1010
theme: {
11-
extend: {},
11+
extend: {
12+
colors: {
13+
background: 'var(--background)',
14+
foreground: 'var(--foreground)',
15+
primary: '#004D61',
16+
secondary: '#FF5031',
17+
tertiary: '#DEE9EA',
18+
quarternary: '#F8F8F8',
19+
black: '#000000',
20+
highlight: '#47A138',
21+
gray: {
22+
background: '#CBCBCB'
23+
}
24+
},
25+
fontFamily: {
26+
inter: ['var(--font-inter)', 'Inter', 'sans-serif']
27+
},
28+
fontSize: {
29+
sm: ['0.812rem', { lineHeight: '0.975rem' }],
30+
'2xl': ['1.562rem', { lineHeight: '1.875rem' }]
31+
},
32+
screens: {
33+
sm: '430',
34+
md: '780'
35+
}
36+
},
1237
},
1338
plugins: [],
1439
};

Diff for: libs/dashboard/data-access/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export * from './lib/dashboard-data-access';
1+
export * from './lib/infrastructure';
2+
export * from './lib/application';
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './transaction.facade'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { DestroyRef, inject, Injectable, signal } from '@angular/core';
2+
import { TransactionRepository } from '../infrastructure';
3+
import { Transaction, TransactionsTypes, Amount, CreateTransaction } from '@fiap-tech-challenge/dashboard-domain';
4+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5+
import { shareReplay } from 'rxjs';
6+
7+
@Injectable({ providedIn: 'root' })
8+
export class TransactionFacade {
9+
#repository = inject(TransactionRepository)
10+
#destroyRef = inject(DestroyRef)
11+
12+
#transactions = signal<Transaction[]>([])
13+
transactions$ = this.#transactions.asReadonly()
14+
15+
#transaction = signal<Transaction | null>(null)
16+
transaction$ = this.#transaction.asReadonly()
17+
18+
#amount = signal<number>(0)
19+
amount$ = this.#amount.asReadonly()
20+
21+
#types = signal<TransactionsTypes[]>([])
22+
types$ = this.#types.asReadonly()
23+
24+
getTransactions() {
25+
return this.#repository.getTransactions()
26+
.pipe(shareReplay<Transaction[]>(),takeUntilDestroyed(this.#destroyRef)).subscribe({
27+
next: (transactions) => this.#transactions.set(transactions)
28+
})
29+
}
30+
31+
createTransaction(transaction: CreateTransaction) {
32+
return this.#repository.createTransaction(transaction).pipe(takeUntilDestroyed(this.#destroyRef)).subscribe({
33+
next: ({ type, value, createdAt }) => {
34+
this.#transactions.set([...this.transactions$(), { type, value, createdAt } as Transaction]);
35+
this.getBalance()
36+
}
37+
})
38+
}
39+
40+
getBalance() {
41+
return this.#repository
42+
.getAmount()
43+
.pipe(shareReplay<Amount>(), takeUntilDestroyed(this.#destroyRef))
44+
.subscribe(({ balance }) => {
45+
this.#amount.set(balance);
46+
});
47+
}
48+
49+
getTypes() {
50+
return this.#repository.getTypes()
51+
.pipe(shareReplay<TransactionsTypes[]>(),takeUntilDestroyed(this.#destroyRef))
52+
.subscribe((types) => this.#types.set(types))
53+
}
54+
}

Diff for: libs/dashboard/data-access/src/lib/dashboard-data-access.ts

-3
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './transaction.repository'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { HttpClient } from '@angular/common/http';
2+
import { inject, Injectable } from '@angular/core';
3+
import { Transaction, TransactionsTypes, Amount, CreateTransaction } from '@fiap-tech-challenge/dashboard-domain';
4+
5+
@Injectable({ providedIn: 'root' })
6+
export class TransactionRepository {
7+
#http = inject(HttpClient)
8+
9+
getTransactions() {
10+
return this.#http.get<Transaction[]>(`/transactions`)
11+
}
12+
13+
createTransaction(transaction: CreateTransaction) {
14+
return this.#http.post<Partial<Transaction>>(`/transactions`, transaction)
15+
}
16+
17+
getAmount() {
18+
return this.#http.get<Amount>('/transactions/balance')
19+
}
20+
21+
getTypes() {
22+
return this.#http.get<TransactionsTypes[]>('/transactions/types')
23+
}
24+
}

Diff for: libs/dashboard/domain/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './lib/dashboard-domain';
1+
export * from './public-api';

Diff for: libs/dashboard/domain/src/lib/dashboard-domain.ts

-3
This file was deleted.

Diff for: libs/dashboard/domain/src/lib/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './types/transaction.interface'
2+
export * from './types/transactions-types.interface';
3+
export * from './types/amount.interface'
4+
export * from './types/create-transaction.interface'
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface Amount {
2+
balance: number;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface CreateTransaction {
2+
value: number;
3+
type: string;
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface Transaction {
2+
value: number;
3+
createdAt: Date;
4+
type: string;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface TransactionsTypes {
2+
value: string;
3+
display: string;
4+
}

Diff for: libs/dashboard/domain/src/public-api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib'

Diff for: libs/dashboard/feature-extract/README.md

+3

Diff for: libs/dashboard/feature-extract/eslint.config.mjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import baseConfig from '../../../eslint.config.mjs';
2+
3+
export default [...baseConfig];

Diff for: libs/dashboard/feature-extract/project.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "feature-extract",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/dashboard/feature-extract/src",
5+
"projectType": "library",
6+
"tags": [],
7+
"// targets": "to see all targets run: nx show project feature-extract --web",
8+
"targets": {}
9+
}

Diff for: libs/dashboard/feature-extract/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './public-api';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<div class="bg-gray-50 p-6 rounded-xl shadow-md max-w-md mx-auto">
2+
<h2 class="text-xl font-bold mb-4">Extrato</h2>
3+
4+
<div *ngFor="let transaction of transactions$()" class="border-l-4 pl-3 mb-3"
5+
[ngClass]="transaction.value >= 0 ? 'border-green-500' : 'border-red-500'">
6+
7+
<p class="text-gray-400 text-sm">{{ transaction.createdAt | date: "dd/mm/yyyy dd:mm" }}</p>
8+
<div class="flex justify-between items-center">
9+
<p class="text-gray-800">{{ transaction.type | titlecase }}</p>
10+
</div>
11+
12+
<p class="text-lg font-bold" [ngClass]="transaction.value >= 0 ? 'text-black' : 'text-red-600'">
13+
{{ transaction.value | currency: 'BRL' }}
14+
</p>
15+
16+
<hr class="border-green-300 mt-1" />
17+
</div>
18+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Component, inject, OnInit } from '@angular/core';
2+
import { CurrencyPipe, DatePipe, NgClass, NgForOf, TitleCasePipe, UpperCasePipe } from '@angular/common';
3+
import { TransactionFacade } from '@fiap-tech-challenge/dashboard-data-access';
4+
5+
@Component({
6+
selector: 'app-dashboard-extract',
7+
templateUrl: './extract.component.html',
8+
standalone: true,
9+
imports: [
10+
NgClass,
11+
NgForOf,
12+
DatePipe,
13+
CurrencyPipe,
14+
TitleCasePipe,
15+
],
16+
})
17+
export class ExtractComponent implements OnInit {
18+
#transactionsFacade = inject(TransactionFacade);
19+
20+
transactions$ = this.#transactionsFacade.transactions$;
21+
22+
ngOnInit() {
23+
this.#transactionsFacade.getTransactions();
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './extract/extract.component'

Diff for: libs/dashboard/feature-extract/src/public-api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/components'

Diff for: libs/dashboard/feature-extract/tsconfig.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"extends": "../../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"module": "commonjs",
5+
"forceConsistentCasingInFileNames": true,
6+
"strict": true,
7+
"importHelpers": true,
8+
"noImplicitOverride": true,
9+
"noImplicitReturns": true,
10+
"noFallthroughCasesInSwitch": true,
11+
"noPropertyAccessFromIndexSignature": true
12+
},
13+
"files": [],
14+
"include": [],
15+
"references": [
16+
{
17+
"path": "./tsconfig.lib.json"
18+
}
19+
]
20+
}

Diff for: libs/dashboard/feature-extract/tsconfig.lib.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "../../../dist/out-tsc",
5+
"declaration": true,
6+
"types": ["node"]
7+
},
8+
"include": ["src/**/*.ts"]
9+
}

Diff for: libs/dashboard/feature-info/README.md

+3

Diff for: libs/dashboard/feature-info/eslint.config.mjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import baseConfig from '../../../eslint.config.mjs';
2+
3+
export default [...baseConfig];

Diff for: libs/dashboard/feature-info/project.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "feature-info",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/dashboard/feature-info/src",
5+
"projectType": "library",
6+
"tags": [],
7+
"// targets": "to see all targets run: nx show project feature-info --web",
8+
"targets": {}
9+
}

Diff for: libs/dashboard/feature-info/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './public-api';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<div class="bg-gradient-to-b from-gray-100 to-gray-200 max-w-md mx-auto h-auto sm:h-[450px] md:min-w-[580px] rounded-lg shadow-lg sm:p-8 p-6 flex flex-col items-center sm:items-start pt-8">
2+
<h2 class="text-3xl text-primary font-semibold text-center sm:text-left">Nova Transação</h2>
3+
4+
<form [formGroup]="form" (ngSubmit)="createTransaction()" class="mt-8 w-full flex flex-col gap-8 items-center sm:items-start">
5+
<div class="w-full">
6+
<label for="category" class="text-sm font-medium text-gray-600">Categoria</label>
7+
<select id="category" formControlName="type" class="mt-2 block w-full p-3 rounded-lg border border-gray-300 shadow-sm focus:ring-primary focus:border-primary">
8+
<option value="" disabled selected>Selecione uma categoria</option>
9+
<option *ngFor="let category of types$()" [value]="category.value">
10+
{{ category.display }}
11+
</option>
12+
</select>
13+
</div>
14+
15+
<div class="w-full sm:w-1/2">
16+
<label for="value" class="text-sm font-medium text-gray-600">Valor</label>
17+
<input
18+
id="value"
19+
type="number"
20+
step="0.01"
21+
formControlName="value"
22+
class="mt-2 block w-full p-3 rounded-lg border border-gray-300 shadow-sm focus:ring-primary focus:border-primary"
23+
/>
24+
<span *ngIf="form.get('value')?.invalid && form.get('value')?.touched" class="text-red-500 text-sm mt-1">Valor inválido</span>
25+
</div>
26+
27+
<div class="flex gap-4 flex-col sm:flex-row w-full justify-center sm:justify-start">
28+
<button type="submit" [disabled]="!form.valid" class="w-full sm:w-auto mt-4 px-6 py-3 bg-primary text-white font-bold rounded-lg shadow-md hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-primary">
29+
Salvar
30+
</button>
31+
<div (click)="handleReset()" class="w-full sm:w-auto mt-4 px-6 py-3 bg-gray-500 text-white font-bold rounded-lg text-center cursor-pointer hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500">
32+
Limpar formulário
33+
</div>
34+
</div>
35+
</form>
36+
</div>

0 commit comments

Comments
 (0)