From a5a645096c4af350e78280cbb6aa5dbfc04ee9de Mon Sep 17 00:00:00 2001 From: FrOZEn-FurY Date: Tue, 20 Aug 2024 21:00:51 +0330 Subject: [PATCH 01/12] Refactored the way upload file wotrks --- .../show-data/show-data.component.html | 9 ++++- .../show-data/show-data.component.scss | 22 +++++++++--- .../show-data/show-data.component.ts | 34 ++++++++++++++++--- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/project/src/app/components/dashboard/show-data/show-data.component.html b/project/src/app/components/dashboard/show-data/show-data.component.html index 3606b4d..34a6f47 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.html +++ b/project/src/app/components/dashboard/show-data/show-data.component.html @@ -1,7 +1,14 @@ @if (user?.role?.toLowerCase() === "admin" || user?.role?.toLowerCase() === "dataadmin") {
+ + - +
} + + diff --git a/project/src/app/components/dashboard/show-data/show-data.component.scss b/project/src/app/components/dashboard/show-data/show-data.component.scss index c28cec1..c485df0 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.scss +++ b/project/src/app/components/dashboard/show-data/show-data.component.scss @@ -2,29 +2,41 @@ @layer utilities { .upload-form { - @apply border-2 border-t-0 rounded-2xl p-4; + @apply border-2 border-t-0 rounded-2xl p-4 text-[2rem] flex flex-row items-center justify-start gap-6; border-color: $neutral-color; } .upload-input { @apply hidden; } .input-label { - @apply cursor-pointer text-[2rem] m-6 p-2; + @apply cursor-pointer p-2; &:hover { @apply border-b border-b-black } } .form-button { - @apply text-[2rem]; color: $primary-color; - border-color: $primary-color; &:hover { color: $bg-color; - background-color: $primary-color; } + + &:disabled { + opacity: 0.5; + + &:hover { + color: $primary-color; + background-color: $bg-color; + } + } + } + .select-group { + @apply focus:outline-none p-2 rounded-2xl; + background-color: $bg-color; + + border: 1px solid black; } } diff --git a/project/src/app/components/dashboard/show-data/show-data.component.ts b/project/src/app/components/dashboard/show-data/show-data.component.ts index ee1a88f..1cee984 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.ts +++ b/project/src/app/components/dashboard/show-data/show-data.component.ts @@ -5,6 +5,15 @@ import {FormsModule} from "@angular/forms"; import {HttpClient} from "@angular/common/http"; import {API_BASE_URL} from "../../../app.config"; +interface Transaction { + TransactionId: number, + sourceAccountId: number, + destinationAccountId: number, + amount: number, + date: string, + type: string +} + @Component({ selector: 'app-show-data', standalone: true, @@ -16,27 +25,44 @@ import {API_BASE_URL} from "../../../app.config"; }) export class ShowDataComponent { user!: User | undefined; + data: Transaction | undefined = undefined; + @ViewChild('labelElement') labelElement!: ElementRef; @ViewChild('inputElement') inputElement!: ElementRef; + @ViewChild('selectElement') selectElement!: ElementRef; constructor(private userService: UserService, private http: HttpClient) { this.user = this.userService.getUser(); } - handleChange() { + handleChange(): void { if (this?.inputElement?.nativeElement?.files && this?.inputElement?.nativeElement?.files?.length > 0) { this.labelElement.nativeElement.textContent = this?.inputElement?.nativeElement?.files[0].name; } } + handleDisabled(): boolean { + return !(this?.inputElement?.nativeElement?.files && this?.inputElement?.nativeElement?.files?.length > 0); + } + onSubmit(): void { const formData: FormData = new FormData(); if (this.inputElement.nativeElement.files) { const file = this.inputElement.nativeElement.files[0]; formData.append('file', file); - this.http.post(API_BASE_URL + 'Transaction/ImportTransactions', formData).subscribe((response) => { - console.log(response); - }) + if (this.selectElement.nativeElement.value === "transaction") { + this.http.post(API_BASE_URL + 'Transaction/ImportTransactions', formData).subscribe((response) => { + console.log(response); + }) + } else if (this.selectElement.nativeElement.value === "account") { + this.http.post(API_BASE_URL + 'Account/ImportAccounts', formData).subscribe((response) => { + console.log(response); + }) + } } } + + updateData(): void { + + } } From c3db589b5045f0eaf54a7e5fcbc8bbf79b98ebf5 Mon Sep 17 00:00:00 2001 From: FrOZEn-FurY Date: Thu, 22 Aug 2024 00:10:44 +0330 Subject: [PATCH 02/12] feat: Completed the upload, and showing data as table --- project/package-lock.json | 6 ++ project/package.json | 1 + .../dashboard/dashboard.component.scss | 4 -- .../show-data/pipes/persian-date.pipe.spec.ts | 8 +++ .../show-data/pipes/persian-date.pipe.ts | 24 ++++++++ .../show-data/pipes/rial-pipe.pipe.spec.ts | 8 +++ .../show-data/pipes/rial-pipe.pipe.ts | 20 +++++++ .../show-data/show-data.component.html | 55 ++++++++++++++----- .../show-data/show-data.component.scss | 47 ++++++++++++++++ .../show-data/show-data.component.ts | 50 +++++++++++++++-- .../directives/blur-click.directive.spec.ts | 8 +++ .../app/directives/blur-click.directive.ts | 22 ++++++++ project/src/declarations/jalaali-js.d.ts | 19 +++++++ 13 files changed, 249 insertions(+), 23 deletions(-) create mode 100644 project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts create mode 100644 project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts create mode 100644 project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts create mode 100644 project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts create mode 100644 project/src/app/directives/blur-click.directive.spec.ts create mode 100644 project/src/app/directives/blur-click.directive.ts create mode 100644 project/src/declarations/jalaali-js.d.ts diff --git a/project/package-lock.json b/project/package-lock.json index 9fe2c47..d774e6c 100644 --- a/project/package-lock.json +++ b/project/package-lock.json @@ -19,6 +19,7 @@ "@angular/router": "^18.1.0", "@angular/ssr": "^18.1.2", "express": "^4.18.2", + "jalaali-js": "^1.2.7", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" @@ -10549,6 +10550,11 @@ "node": ">=8" } }, + "node_modules/jalaali-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.7.tgz", + "integrity": "sha512-gE+YHWSbygYAoJa+Xg8LWxGILqFOxZSBQQw39ghel01fVFUxV7bjL0x1JFsHcLQ3uPjvn81HQMa+kxwyPWnxGQ==" + }, "node_modules/jasmine-core": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz", diff --git a/project/package.json b/project/package.json index c77c2fc..16b87cc 100644 --- a/project/package.json +++ b/project/package.json @@ -24,6 +24,7 @@ "@angular/router": "^18.1.0", "@angular/ssr": "^18.1.2", "express": "^4.18.2", + "jalaali-js": "^1.2.7", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.3" diff --git a/project/src/app/components/dashboard/dashboard.component.scss b/project/src/app/components/dashboard/dashboard.component.scss index 8392e2e..eb90c81 100644 --- a/project/src/app/components/dashboard/dashboard.component.scss +++ b/project/src/app/components/dashboard/dashboard.component.scss @@ -19,7 +19,3 @@ background-color: $bg-color; } } - -.dashboard-content { - align-self: start; -} diff --git a/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts new file mode 100644 index 0000000..51860f8 --- /dev/null +++ b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.spec.ts @@ -0,0 +1,8 @@ +import { PersianDatePipe } from './persian-date.pipe'; + +describe('PersianDatePipe', () => { + it('create an instance', () => { + const pipe = new PersianDatePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts new file mode 100644 index 0000000..d41edd0 --- /dev/null +++ b/project/src/app/components/dashboard/show-data/pipes/persian-date.pipe.ts @@ -0,0 +1,24 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import * as jalaali from 'jalaali-js'; + +@Pipe({ + name: 'persianDate', + standalone: true +}) +export class PersianDatePipe implements PipeTransform { + + transform(value: string): string { + if (!value) return ''; + + const date = new Date(value); + const jDate = jalaali.toJalaali(date.getFullYear(), date.getMonth() + 1, date.getDate()); + + // Format the Persian date as desired (YYYY/MM/DD) + return `${jDate.jy}/${this.padZero(jDate.jm)}/${this.padZero(jDate.jd)}`; + } + + // Helper method to pad single digits with a leading zero + private padZero(value: number): string { + return value < 10 ? '0' + value : value.toString(); + } +} diff --git a/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts new file mode 100644 index 0000000..b48d723 --- /dev/null +++ b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.spec.ts @@ -0,0 +1,8 @@ +import { RialPipePipe } from './rial-pipe.pipe'; + +describe('RialPipePipe', () => { + it('create an instance', () => { + const pipe = new RialPipePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts new file mode 100644 index 0000000..2a64b2f --- /dev/null +++ b/project/src/app/components/dashboard/show-data/pipes/rial-pipe.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'rialPipe', + standalone: true +}) +export class RialPipePipe implements PipeTransform { + + transform(value: number): string { + value /= 10; + let result = String(value); + let cntr = result.length - 3; + while (cntr > 0) { + result = result.substring(0, cntr) + ',' + result.substring(cntr, result.length); + cntr -= 3; + } + return result + ' تومان'; + } + +} diff --git a/project/src/app/components/dashboard/show-data/show-data.component.html b/project/src/app/components/dashboard/show-data/show-data.component.html index 34a6f47..804f004 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.html +++ b/project/src/app/components/dashboard/show-data/show-data.component.html @@ -1,14 +1,43 @@ -@if (user?.role?.toLowerCase() === "admin" || user?.role?.toLowerCase() === "dataadmin") { -
- - - - - -
-} +
+ @if (user?.role?.toLowerCase() === "admin" || user?.role?.toLowerCase() === "dataadmin") { +
+ + + + + +
+ } - +
+
+ + + + + + + + + + + + + @for (trans of data; track trans.TransactionId) { + + + + + + + + } @empty { +

هیچ داده ای برای نمایش یافت نشد.

+ } + +
حساب مبداحساب مقصدمبلغ تراکنشتاریخنوع تراکنش
{{ trans.sourceAccountId }}{{ trans.destinationAccountId }}{{ trans.amount | rialPipe }}{{ trans.date | persianDate }}{{ trans.type }}
+
+
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.scss b/project/src/app/components/dashboard/show-data/show-data.component.scss index c485df0..d08f164 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.scss +++ b/project/src/app/components/dashboard/show-data/show-data.component.scss @@ -1,6 +1,9 @@ @import "../../../../variables"; @layer utilities { + .data-show-container { + @apply flex flex-col justify-start items-center gap-4 h-[100vh]; + } .upload-form { @apply border-2 border-t-0 rounded-2xl p-4 text-[2rem] flex flex-row items-center justify-start gap-6; border-color: $neutral-color; @@ -39,4 +42,48 @@ border: 1px solid black; } + .show-button { + @apply text-[2rem] self-center border rounded-3xl p-3 transition-all duration-300 ease-in-out; + color: $primary-color; + border-color: $primary-color; + + &:hover { + @apply transition-all duration-300 ease-in-out; + background-color: $primary-color; + color: $bg-color; + } + } + .table-container { + @apply absolute p-6 justify-center items-center rounded-2xl hidden text-[1.5rem] overflow-auto; + background-color: $primary-color; + color: $bg-color; + + inline-size: 70rem; + block-size: 50rem; + + inset-block-start: calc(50vh - 25rem); + inset-inline-start: calc(50vw - 35rem); + + scroll-behavior: smooth; + scrollbar-width: thin; + scrollbar-gutter: stable; + scrollbar-color: $secondary-color $primary-color; + } + .table { + @apply border-separate border-spacing-4 table-auto mt-10; + + thead { + @apply top-0 sticky; + background-color: $secondary-color; + } + + td, th { + @apply border-b p-1; + border-color: $bg-color; + } + } + .xicon { + @apply absolute w-[4rem] h-[4rem] top-0 right-0; + color: $bg-color; + } } diff --git a/project/src/app/components/dashboard/show-data/show-data.component.ts b/project/src/app/components/dashboard/show-data/show-data.component.ts index 1cee984..4f68c9a 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.ts +++ b/project/src/app/components/dashboard/show-data/show-data.component.ts @@ -1,9 +1,14 @@ -import {Component, ElementRef, ViewChild} from '@angular/core'; +import {Component, ElementRef, EventEmitter, HostListener, Output, ViewChild} from '@angular/core'; import {UserService} from "../../../services/user/user.service"; import User from "../../../interfaces/user"; import {FormsModule} from "@angular/forms"; import {HttpClient} from "@angular/common/http"; import {API_BASE_URL} from "../../../app.config"; +import {RialPipePipe} from "./pipes/rial-pipe.pipe"; +import {PersianDatePipe} from "./pipes/persian-date.pipe"; +import {heroXMark} from "@ng-icons/heroicons/outline"; +import {NgIconComponent, provideIcons} from "@ng-icons/core"; +import {BlurClickDirective} from "../../../directives/blur-click.directive"; interface Transaction { TransactionId: number, @@ -18,18 +23,25 @@ interface Transaction { selector: 'app-show-data', standalone: true, imports: [ - FormsModule + FormsModule, + RialPipePipe, + PersianDatePipe, + NgIconComponent, + BlurClickDirective ], templateUrl: './show-data.component.html', - styleUrl: './show-data.component.scss' + styleUrl: './show-data.component.scss', + providers: [provideIcons({heroXMark})] }) export class ShowDataComponent { user!: User | undefined; - data: Transaction | undefined = undefined; + data: Transaction[] | undefined = undefined; + dataGot = false; @ViewChild('labelElement') labelElement!: ElementRef; @ViewChild('inputElement') inputElement!: ElementRef; @ViewChild('selectElement') selectElement!: ElementRef; + @ViewChild('dataElement') dataElement!: ElementRef; constructor(private userService: UserService, private http: HttpClient) { this.user = this.userService.getUser(); @@ -47,15 +59,16 @@ export class ShowDataComponent { onSubmit(): void { const formData: FormData = new FormData(); + const token: string | null = this.getToken(); if (this.inputElement.nativeElement.files) { const file = this.inputElement.nativeElement.files[0]; formData.append('file', file); if (this.selectElement.nativeElement.value === "transaction") { - this.http.post(API_BASE_URL + 'Transaction/ImportTransactions', formData).subscribe((response) => { + this.http.post(API_BASE_URL + 'Transaction/ImportTransactions', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { console.log(response); }) } else if (this.selectElement.nativeElement.value === "account") { - this.http.post(API_BASE_URL + 'Account/ImportAccounts', formData).subscribe((response) => { + this.http.post(API_BASE_URL + 'Account/ImportAccounts', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { console.log(response); }) } @@ -63,6 +76,31 @@ export class ShowDataComponent { } updateData(): void { + this.dataGot = false; + const token: string | null = this.getToken(); + this.http.get(API_BASE_URL + 'Transaction/GetAllTransactions', {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { + this.data = response; + this.dataGot = true; + }) + } + + showData(): void { + if (this.data === undefined) { + this.updateData(); + } + this.dataElement.nativeElement.style.display = 'flex'; + } + + handleClose(): void { + this.dataElement.nativeElement.style.display = 'none'; + } + + getToken(): string | null { + let token = localStorage.getItem("token"); + if (token) { + token = token.substring(1, token.length - 1); + } + return token; } } diff --git a/project/src/app/directives/blur-click.directive.spec.ts b/project/src/app/directives/blur-click.directive.spec.ts new file mode 100644 index 0000000..6bfb9d8 --- /dev/null +++ b/project/src/app/directives/blur-click.directive.spec.ts @@ -0,0 +1,8 @@ +import { BlurClickDirective } from './blur-click.directive'; + +describe('BlurClickDirective', () => { + it('should create an instance', () => { + const directive = new BlurClickDirective(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/project/src/app/directives/blur-click.directive.ts b/project/src/app/directives/blur-click.directive.ts new file mode 100644 index 0000000..87e2347 --- /dev/null +++ b/project/src/app/directives/blur-click.directive.ts @@ -0,0 +1,22 @@ +import {Directive, ElementRef, EventEmitter, HostListener, Output} from '@angular/core'; + +@Directive({ + selector: '[appBlurClick]', + standalone: true +}) +export class BlurClickDirective { + @Output() blurClick = new EventEmitter(); + + constructor(private element: ElementRef) {} + + @HostListener('document:click', ['$event.target']) + handleBlur(element: HTMLElement) { + console.log(this.element.nativeElement.style.display === 'flex') + console.log(!this.element.nativeElement.contains(element)) + console.log(element.dataset) + if (this.element.nativeElement.style.display === 'flex' && !this.element.nativeElement.contains(element) + && element.dataset['triggerButton'] === undefined) { + this.blurClick.emit(); + } + } +} diff --git a/project/src/declarations/jalaali-js.d.ts b/project/src/declarations/jalaali-js.d.ts new file mode 100644 index 0000000..f0fee96 --- /dev/null +++ b/project/src/declarations/jalaali-js.d.ts @@ -0,0 +1,19 @@ +declare module 'jalaali-js' { + export interface JalaaliDate { + jy: number; // Jalali year + jm: number; // Jalali month + jd: number; // Jalali day + } + + export function toJalaali( + gy: number, + gm: number, + gd: number + ): JalaaliDate; + + export function toGregorian( + jy: number, + jm: number, + jd: number + ): { gy: number; gm: number; gd: number }; +} From 2bff88c56f31a0fe8ac3cd407f8680c1a96aed80 Mon Sep 17 00:00:00 2001 From: FrOZEn-FurY Date: Sun, 25 Aug 2024 04:55:29 +0330 Subject: [PATCH 03/12] feat: Completed the manage user section --- project/src/app/app.routes.ts | 3 +- .../manage-users/manage-users.component.html | 90 ++++++++++++++- .../manage-users/manage-users.component.scss | 86 +++++++++++++++ .../manage-users/manage-users.component.ts | 103 +++++++++++++++++- .../app/directives/blur-click.directive.ts | 3 - .../app/guards/admin/is-admin.guard.spec.ts | 17 +++ .../src/app/guards/admin/is-admin.guard.ts | 14 +++ .../modify-user/modify-user.service.ts | 13 +++ 8 files changed, 321 insertions(+), 8 deletions(-) create mode 100644 project/src/app/guards/admin/is-admin.guard.spec.ts create mode 100644 project/src/app/guards/admin/is-admin.guard.ts diff --git a/project/src/app/app.routes.ts b/project/src/app/app.routes.ts index 44270fa..3a583b1 100644 --- a/project/src/app/app.routes.ts +++ b/project/src/app/app.routes.ts @@ -7,13 +7,14 @@ import {EditProfileComponent} from "./components/dashboard/edit-profile/edit-pro import {ChangePasswordComponent} from "./components/dashboard/change-password/change-password.component"; import {ManageUsersComponent} from "./components/dashboard/manage-users/manage-users.component"; import {ShowDataComponent} from "./components/dashboard/show-data/show-data.component"; +import {isAdminGuard} from "./guards/admin/is-admin.guard"; export const routes: Routes = [ {path: 'dashboard', component: DashboardComponent, children: [ {path: 'profile', component: ProfileComponent}, {path: 'edit-profile', component: EditProfileComponent}, {path: 'change-password', component: ChangePasswordComponent}, - {path: 'manage-users', component: ManageUsersComponent}, + {path: 'manage-users', component: ManageUsersComponent, canActivate: [isAdminGuard]}, {path: 'show-data', component: ShowDataComponent}, ], canActivate: [loggedInGuard], data: { requiresAuth: true } }, {path: 'login', component: LoginComponent, canActivate: [loggedInGuard], data: { requiresAuth: false }}, diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.html b/project/src/app/components/dashboard/manage-users/manage-users.component.html index 544c483..607218e 100644 --- a/project/src/app/components/dashboard/manage-users/manage-users.component.html +++ b/project/src/app/components/dashboard/manage-users/manage-users.component.html @@ -1 +1,89 @@ -

manage-users works!

+
+
+ + + +
+ +
+ +
+ @for (user of finalUsers; track user.userName) { +
+

{{ user.firstName }} {{ user.lastName }}

+

{{ user.userName }}

+

{{ user.email }}

+
+ + +
+
+ } +
+
+ +
+ + + + + + + + + + + + + + + + + + + + +
diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.scss b/project/src/app/components/dashboard/manage-users/manage-users.component.scss index e69de29..78a7691 100644 --- a/project/src/app/components/dashboard/manage-users/manage-users.component.scss +++ b/project/src/app/components/dashboard/manage-users/manage-users.component.scss @@ -0,0 +1,86 @@ +@import '../../../../variables'; + +@layer components { + .contain { + @apply flex flex-col items-center justify-start text-[2rem]; + + block-size: 100vh; + } + .search-form { + @apply p-4 flex flex-row items-center gap-8; + + input { + @apply p-4 focus:outline-none rounded-2xl border-b transition-all duration-200 delay-75 ease-in-out; + border-color: $neutral-color; + + &:hover { + @apply transition-all duration-200 delay-75 ease-in-out; + border-color: $secondary-color; + } + } + + button[type="button"] { + @apply flex flex-row items-center gap-2; + + .plusicon { + @apply w-[2.2rem] h-[2.2rem] + } + } + } + .line { + @apply border border-black w-full m-4; + } + .users-container { + @apply flex flex-row flex-wrap justify-center gap-6 p-8; + } + .user-container { + @apply flex flex-col items-center gap-4 p-4 border border-black rounded-2xl; + + h3 { + @apply text-[1.8rem] + } + + h4 { + @apply text-[1.6rem] + } + + form { + @apply flex flex-row gap-4; + + select { + @apply border border-black rounded-2xl text-[1.5rem]; + } + } + } + .button { + @apply p-4 border transition-all duration-300 ease-in-out rounded-3xl text-[1.5rem]; + background-color: $primary-color; + color: $bg-color; + + &:hover { + @apply transition-all duration-300 ease-in-out; + background-color: $secondary-color; + } + } + .form-container { + @apply hidden absolute p-6 flex-col justify-center items-center text-[2rem] rounded-2xl; + background-color: $primary-color; + color: $bg-color; + + inline-size: 50rem; + block-size: 85rem; + + inset-block-start: calc(50vh - 42.5rem); + inset-inline-end: calc(50vw - 25rem); + + .xicon { + @apply absolute w-[3.5rem] h-[3.5rem] top-0 right-0 hover:cursor-pointer; + color: $bg-color; + } + + select { + @apply p-2 m-2 focus:outline-none rounded-2xl; + color: black + } + } +} diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.ts b/project/src/app/components/dashboard/manage-users/manage-users.component.ts index 0b3fc93..dbe1b9e 100644 --- a/project/src/app/components/dashboard/manage-users/manage-users.component.ts +++ b/project/src/app/components/dashboard/manage-users/manage-users.component.ts @@ -1,12 +1,109 @@ -import { Component } from '@angular/core'; +import {Component, ElementRef, ViewChild} from '@angular/core'; +import User from "../../../interfaces/user"; +import {HttpClient} from "@angular/common/http"; +import {API_BASE_URL} from "../../../app.config"; +import {ModifyUserService} from "../../../services/modify-user/modify-user.service"; +import {NgIconComponent, provideIcons} from "@ng-icons/core"; +import {heroUserPlus, heroXMark} from "@ng-icons/heroicons/outline"; +import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms"; +import {UserService} from "../../../services/user/user.service"; +import {Router} from "@angular/router"; +import {BlurClickDirective} from "../../../directives/blur-click.directive"; @Component({ selector: 'app-manage-users', standalone: true, - imports: [], + imports: [NgIconComponent, FormsModule, ReactiveFormsModule, BlurClickDirective], templateUrl: './manage-users.component.html', - styleUrl: './manage-users.component.scss' + styleUrl: './manage-users.component.scss', + providers: [provideIcons({heroUserPlus, heroXMark})] }) export class ManageUsersComponent { + users: User[] | undefined = undefined; + finalUsers: User[] | undefined = undefined; + formGroup = new FormGroup({ + firstName: new FormControl('', Validators.required), + lastName: new FormControl('', Validators.required), + email: new FormControl('', [Validators.required, Validators.email]), + userName: new FormControl('', Validators.required), + password: new FormControl('', Validators.required), + role: new FormControl('Admin', Validators.required), + }) + + @ViewChild('formElement') formElement!: ElementRef; + @ViewChild('searchElement') searchElement!: ElementRef; + + constructor(private modifyService: ModifyUserService, private http: HttpClient, private userService: UserService, + private router: Router) {} + + ngOnInit(): void { + const token = this.getToken(); + this.http.get(API_BASE_URL + 'Identity/GetUsers', {headers: {'Authorization': "bearer " + token}}) + .subscribe((response) => { + this.users = response; + this.finalUsers = response; + }); + } + + handleChangeRole(user: User): void { + const index = this?.finalUsers?.indexOf(user); + if (index !== -1 && index !== undefined && this.finalUsers !== undefined) { + const selected = (document.getElementById(this.finalUsers[index].userName!) as HTMLSelectElement).value; + if (selected === this.finalUsers[index].role!.toLowerCase()) { + return; + } + this.finalUsers[index].role = selected; + this.modifyService.alterRole(this.finalUsers[index].userName!, selected); + const user = this.userService.getUser(); + if (user?.userName === this.finalUsers[index].userName!) { + user.role = selected; + this.userService.setUser(user); + this.router.navigateByUrl('dashboard/profile'); + } + } + } + + getToken(): string | null { + let token = localStorage.getItem("token"); + if (token) { + token = token.substring(1, token.length - 1); + } + return token; + } + + handleAdd(): void { + if (this.formGroup.valid) { + const data = { + firstName: this.formGroup.value.firstName, + lastName: this.formGroup.value.lastName, + userName: this.formGroup.value.userName, + email: this.formGroup.value.email, + password: this.formGroup.value.password, + role: this.formGroup.value.role, + } + const token = this.getToken(); + this.http.post(API_BASE_URL + 'Identity/Signup', data, {headers: {'Authorization': "Bearer " + token}}) + .subscribe((res) => { + this.finalUsers?.push(res); + this.handleClose(); + }); + } else { + console.log(this.formGroup.value); + } + } + + handleShowForm(): void { + this.formElement.nativeElement.style.display = 'flex'; + } + + handleClose(): void { + this.formElement.nativeElement.style.display = 'none'; + } + + handleSearch() { + const value = this.searchElement.nativeElement.value; + this.finalUsers = this.users?.filter(user => user.userName?.includes(value) || + user.firstName?.includes(value) || user.lastName?.includes(value) || user.email?.includes(value)); + } } diff --git a/project/src/app/directives/blur-click.directive.ts b/project/src/app/directives/blur-click.directive.ts index 87e2347..60adafe 100644 --- a/project/src/app/directives/blur-click.directive.ts +++ b/project/src/app/directives/blur-click.directive.ts @@ -11,9 +11,6 @@ export class BlurClickDirective { @HostListener('document:click', ['$event.target']) handleBlur(element: HTMLElement) { - console.log(this.element.nativeElement.style.display === 'flex') - console.log(!this.element.nativeElement.contains(element)) - console.log(element.dataset) if (this.element.nativeElement.style.display === 'flex' && !this.element.nativeElement.contains(element) && element.dataset['triggerButton'] === undefined) { this.blurClick.emit(); diff --git a/project/src/app/guards/admin/is-admin.guard.spec.ts b/project/src/app/guards/admin/is-admin.guard.spec.ts new file mode 100644 index 0000000..19aa27d --- /dev/null +++ b/project/src/app/guards/admin/is-admin.guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { CanActivateFn } from '@angular/router'; + +import { isAdminGuard } from './is-admin.guard'; + +describe('isAdminGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => isAdminGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/project/src/app/guards/admin/is-admin.guard.ts b/project/src/app/guards/admin/is-admin.guard.ts new file mode 100644 index 0000000..69b3e4a --- /dev/null +++ b/project/src/app/guards/admin/is-admin.guard.ts @@ -0,0 +1,14 @@ +import {CanActivateFn, Router} from '@angular/router'; +import {inject} from "@angular/core"; +import {UserService} from "../../services/user/user.service"; + +export const isAdminGuard: CanActivateFn = (route, state) => { + const userService = inject(UserService); + const router = inject(Router); + const user = userService.getUser(); + if (!user || user.role?.toLowerCase() !== "admin") { + router.navigateByUrl('dashboard/profile') + return false; + } + return true; +}; diff --git a/project/src/app/services/modify-user/modify-user.service.ts b/project/src/app/services/modify-user/modify-user.service.ts index f08af8b..723aec7 100644 --- a/project/src/app/services/modify-user/modify-user.service.ts +++ b/project/src/app/services/modify-user/modify-user.service.ts @@ -50,4 +50,17 @@ export class ModifyUserService { console.log(response); }); } + + alterRole(userName: string, role: string): void { + const token = this.getToken(); + const data = { + userName, + role + } + + this.http.put(API_BASE_URL + 'Identity/ChangeRole', data, {headers: {'Authorization': "Bearer " + token}}) + .subscribe((response) => { + console.log(response); + }) + } } From b8eb846b3f0d573cb5fd85c06390602b3007bd44 Mon Sep 17 00:00:00 2001 From: FrOZEn-FurY Date: Sun, 25 Aug 2024 16:32:34 +0330 Subject: [PATCH 04/12] Started rafactoring ui/ux --- .../manage-users/manage-users.component.scss | 7 ++++--- .../dashboard/navbar/navbar.component.scss | 6 ++++-- .../show-data/show-data.component.scss | 7 ++++--- .../app/components/login/login.component.scss | 18 +++++++++--------- project/src/variables.scss | 8 +++----- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.scss b/project/src/app/components/dashboard/manage-users/manage-users.component.scss index 78a7691..3d0351d 100644 --- a/project/src/app/components/dashboard/manage-users/manage-users.component.scss +++ b/project/src/app/components/dashboard/manage-users/manage-users.component.scss @@ -11,11 +11,11 @@ input { @apply p-4 focus:outline-none rounded-2xl border-b transition-all duration-200 delay-75 ease-in-out; - border-color: $neutral-color; + border-color: black; &:hover { @apply transition-all duration-200 delay-75 ease-in-out; - border-color: $secondary-color; + border-color: $bg-color; } } @@ -59,7 +59,8 @@ &:hover { @apply transition-all duration-300 ease-in-out; - background-color: $secondary-color; + background-color: $bg-color; + color: black; } } .form-container { diff --git a/project/src/app/components/dashboard/navbar/navbar.component.scss b/project/src/app/components/dashboard/navbar/navbar.component.scss index 8fdb2ba..529255e 100644 --- a/project/src/app/components/dashboard/navbar/navbar.component.scss +++ b/project/src/app/components/dashboard/navbar/navbar.component.scss @@ -6,7 +6,7 @@ } .navbar-item { @apply text-[2rem] hover:cursor-pointer border-b-2 p-4 transition-all duration-300 delay-75 ease-in-out; - border-bottom-color: $secondary-color; + border-bottom-color: $bg-color; &:hover { @apply transition-all duration-300 delay-75 ease-in-out; @@ -16,6 +16,8 @@ } } .active { - background: $secondary-color; + background: $bg-color; + color: black; + border-bottom-color: black; } } diff --git a/project/src/app/components/dashboard/show-data/show-data.component.scss b/project/src/app/components/dashboard/show-data/show-data.component.scss index d08f164..1228ed5 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.scss +++ b/project/src/app/components/dashboard/show-data/show-data.component.scss @@ -6,7 +6,7 @@ } .upload-form { @apply border-2 border-t-0 rounded-2xl p-4 text-[2rem] flex flex-row items-center justify-start gap-6; - border-color: $neutral-color; + border-color: black; } .upload-input { @apply hidden; @@ -67,14 +67,15 @@ scroll-behavior: smooth; scrollbar-width: thin; scrollbar-gutter: stable; - scrollbar-color: $secondary-color $primary-color; + scrollbar-color: $bg-color $primary-color; } .table { @apply border-separate border-spacing-4 table-auto mt-10; thead { @apply top-0 sticky; - background-color: $secondary-color; + background-color: $bg-color; + color: black; } td, th { diff --git a/project/src/app/components/login/login.component.scss b/project/src/app/components/login/login.component.scss index 5d65583..f07158f 100644 --- a/project/src/app/components/login/login.component.scss +++ b/project/src/app/components/login/login.component.scss @@ -9,8 +9,8 @@ } .input-container { @apply flex flex-row justify-start items-center w-[24rem] h-[2.7rem] border-b-[0.1rem] gap-[0.5rem] mt-[2rem] relative; - color: $neutral-color; - border-bottom-color: $neutral-color; + color: black; + border-bottom-color: black; &:focus-within { color: $bg-color; @@ -65,15 +65,15 @@ @apply input-container; &:focus-within { - color: $secondary-color; - border-bottom-color: $secondary-color; + color: $bg-color; + border-bottom-color: $bg-color; } input { @apply flex-grow; &::placeholder { - color: $bg-color; + color: $primary-color; } } } @@ -81,15 +81,15 @@ @apply input-container; &:focus-within { - color: $secondary-color; - border-bottom-color: $secondary-color; + color: $bg-color; + border-bottom-color: $bg-color; } input { @apply flex-grow; &::placeholder { - color: $bg-color; + color: $primary-color; } } } @@ -126,7 +126,7 @@ } .svg-form-lg { @apply w-[100%] h-[100%] fill-none basis-1/2 rounded-l-3xl; - background: $secondary-color; + background-color: rgb(18 91 154 / 80%); } .form-container-lg { @apply basis-1/2 rounded-r-3xl flex flex-row justify-center items-center; diff --git a/project/src/variables.scss b/project/src/variables.scss index 5e27545..4bd7cdd 100644 --- a/project/src/variables.scss +++ b/project/src/variables.scss @@ -5,12 +5,10 @@ // Colors $bg-color: #FDFDFD; -$neutral-color: #666666; -$primary-color: #002B5B; -$secondary-color: #4F81BD; +$primary-color: rgb(18, 91, 154); $success-color: #4FBD70; $accent-color: #E9B91B; -$error-color: #B4131F; +$error-color: #F05A7E; // Base classes @layer components { @@ -27,7 +25,7 @@ $error-color: #B4131F; background-color: $primary-color; color: $bg-color; - box-shadow: 0 0 1.2rem $neutral-color; + box-shadow: 0 0 1.2rem black; } .form-label { @apply text-[3rem] m-5 border-b-2 p-4 transition-all duration-300 delay-75 ease-in-out From 2e3c43cee916cfc01f982a4967cfdd9402e054d1 Mon Sep 17 00:00:00 2001 From: parsa Date: Mon, 26 Aug 2024 04:58:04 +0330 Subject: [PATCH 05/12] feat : change loptop login design (add bganimrion and more) --- project/package-lock.json | 14 +- .../components/bg-gif/bg-gif.component.html | 21 ++ .../components/bg-gif/bg-gif.component.scss | 205 ++++++++++++++++++ .../bg-gif/bg-gif.component.spec.ts | 23 ++ .../app/components/bg-gif/bg-gif.component.ts | 12 + .../app/components/login/login.component.html | 76 +++---- .../app/components/login/login.component.scss | 48 ++-- project/src/styles.scss | 18 +- project/src/variables.scss | 6 +- 9 files changed, 354 insertions(+), 69 deletions(-) create mode 100644 project/src/app/components/bg-gif/bg-gif.component.html create mode 100644 project/src/app/components/bg-gif/bg-gif.component.scss create mode 100644 project/src/app/components/bg-gif/bg-gif.component.spec.ts create mode 100644 project/src/app/components/bg-gif/bg-gif.component.ts diff --git a/project/package-lock.json b/project/package-lock.json index d774e6c..fe33110 100644 --- a/project/package-lock.json +++ b/project/package-lock.json @@ -6566,10 +6566,11 @@ } }, "node_modules/axios": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", - "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -11768,10 +11769,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" diff --git a/project/src/app/components/bg-gif/bg-gif.component.html b/project/src/app/components/bg-gif/bg-gif.component.html new file mode 100644 index 0000000..70fc429 --- /dev/null +++ b/project/src/app/components/bg-gif/bg-gif.component.html @@ -0,0 +1,21 @@ +
+ + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/project/src/app/components/bg-gif/bg-gif.component.scss b/project/src/app/components/bg-gif/bg-gif.component.scss new file mode 100644 index 0000000..a5eb9be --- /dev/null +++ b/project/src/app/components/bg-gif/bg-gif.component.scss @@ -0,0 +1,205 @@ + + +@keyframes move { + 100% { + transform: translate3d(0, 0, 1px) rotate(360deg); + } +} + +.background { + position: fixed; + width: 100vw; + height: 100vh; + top: 0; + left: 0; + background: #fdfdfd; + overflow: hidden; + z-index: -1; +} + +.background span { + width: 1vmin; + height: 1vmin; + border-radius: 1vmin; + backface-visibility: hidden; + position: absolute; + animation: move; + animation-duration: 38; + animation-timing-function: linear; + animation-iteration-count: infinite; +} + + +.background span:nth-child(0) { + color: #e9b91b; + top: 44%; + left: 37%; + animation-duration: 62s; + animation-delay: -45s; + transform-origin: -4vw -4vh; + box-shadow: 2vmin 0 1.179326042713791vmin currentColor; +} +.background span:nth-child(1) { + color: #4f86c9; + top: 23%; + left: 97%; + animation-duration: 82s; + animation-delay: -25s; + transform-origin: 5vw 25vh; + box-shadow: -2vmin 0 0.7106015815700037vmin currentColor; +} +.background span:nth-child(2) { + color: #e9b91b; + top: 3%; + left: 64%; + animation-duration: 121s; + animation-delay: -58s; + transform-origin: 18vw -7vh; + box-shadow: -2vmin 0 0.2600782522780347vmin currentColor; +} +.background span:nth-child(3) { + color: #e9b91b; + top: 1%; + left: 65%; + animation-duration: 14s; + animation-delay: -34s; + transform-origin: -23vw 10vh; + box-shadow: -2vmin 0 0.8925616875372739vmin currentColor; +} +.background span:nth-child(4) { + color: #e9b91b; + top: 64%; + left: 96%; + animation-duration: 111s; + animation-delay: -36s; + transform-origin: -5vw 15vh; + box-shadow: -2vmin 0 0.3975511244379719vmin currentColor; +} +.background span:nth-child(5) { + color: #4f86c9; + top: 35%; + left: 88%; + animation-duration: 83s; + animation-delay: -19s; + transform-origin: 4vw 6vh; + box-shadow: -2vmin 0 0.4574674663634355vmin currentColor; +} +.background span:nth-child(6) { + color: #002b5b; + top: 17%; + left: 70%; + animation-duration: 116s; + animation-delay: -13s; + transform-origin: 10vw -22vh; + box-shadow: 2vmin 0 0.5994571422697834vmin currentColor; +} +.background span:nth-child(7) { + color: #e9b91b; + top: 8%; + left: 30%; + animation-duration: 12s; + animation-delay: -63s; + transform-origin: 20vw -20vh; + box-shadow: -2vmin 0 0.944790036995725vmin currentColor; +} +.background span:nth-child(8) { + color: #002b5b; + top: 58%; + left: 45%; + animation-duration: 23s; + animation-delay: -101s; + transform-origin: -20vw -2vh; + box-shadow: -2vmin 0 1.113637056359262vmin currentColor; +} +.background span:nth-child(9) { + color: #4f86c9; + top: 98%; + left: 80%; + animation-duration: 103s; + animation-delay: -9s; + transform-origin: -4vw 24vh; + box-shadow: -2vmin 0 0.8466328436131629vmin currentColor; +} +.background span:nth-child(10) { + color: #e9b91b; + top: 91%; + left: 35%; + animation-duration: 13s; + animation-delay: -85s; + transform-origin: -1vw -18vh; + box-shadow: -2vmin 0 0.6988589961678999vmin currentColor; +} +.background span:nth-child(11) { + color: #4f86c9; + top: 12%; + left: 38%; + animation-duration: 124s; + animation-delay: -80s; + transform-origin: -10vw -12vh; + box-shadow: -2vmin 0 1.0281140568796288vmin currentColor; +} +.background span:nth-child(12) { + color: #4f86c9; + top: 65%; + left: 47%; + animation-duration: 14s; + animation-delay: -20s; + transform-origin: 13vw 19vh; + box-shadow: 2vmin 0 0.4922053968944644vmin currentColor; +} +.background span:nth-child(13) { + color: #002b5b; + top: 1%; + left: 85%; + animation-duration: 119s; + animation-delay: -120s; + transform-origin: -5vw 1vh; + box-shadow: 2vmin 0 0.89657619610927vmin currentColor; +} +.background span:nth-child(14) { + color: #4f86c9; + top: 98%; + left: 67%; + animation-duration: 63s; + animation-delay: -98s; + transform-origin: 17vw -24vh; + box-shadow: -2vmin 0 0.2674072793462621vmin currentColor; +} +.background span:nth-child(15) { + color: #4f86c9; + top: 47%; + left: 22%; + animation-duration: 114s; + animation-delay: -21s; + transform-origin: 20vw 6vh; + box-shadow: 2vmin 0 1.1494681355574716vmin currentColor; +} +.background span:nth-child(16) { + color: #4f86c9; + top: 17%; + left: 3%; + animation-duration: 29s; + animation-delay: -82s; + transform-origin: 15vw 22vh; + box-shadow: -2vmin 0 0.7033084944562273vmin currentColor; +} +.background span:nth-child(17) { + color: #e9b91b; + top: 18%; + left: 31%; + animation-duration: 26s; + animation-delay: -97s; + transform-origin: 20vw -15vh; + box-shadow: 2vmin 0 0.3154577920055257vmin currentColor; +} +.background span:nth-child(18) { + color: #e9b91b; + top: 72%; + left: 46%; + animation-duration: 85s; + animation-delay: -42s; + transform-origin: 14vw 6vh; + box-shadow: 2vmin 0 0.5774089805359588vmin currentColor; +} + + diff --git a/project/src/app/components/bg-gif/bg-gif.component.spec.ts b/project/src/app/components/bg-gif/bg-gif.component.spec.ts new file mode 100644 index 0000000..098313b --- /dev/null +++ b/project/src/app/components/bg-gif/bg-gif.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BgGifComponent } from './bg-gif.component'; + +describe('BgGifComponent', () => { + let component: BgGifComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BgGifComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BgGifComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/project/src/app/components/bg-gif/bg-gif.component.ts b/project/src/app/components/bg-gif/bg-gif.component.ts new file mode 100644 index 0000000..9432fd2 --- /dev/null +++ b/project/src/app/components/bg-gif/bg-gif.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-bg-gif', + standalone: true, + imports: [], + templateUrl: './bg-gif.component.html', + styleUrl: './bg-gif.component.scss' +}) +export class BgGifComponent { + +} diff --git a/project/src/app/components/login/login.component.html b/project/src/app/components/login/login.component.html index 8fbbd13..fc62951 100644 --- a/project/src/app/components/login/login.component.html +++ b/project/src/app/components/login/login.component.html @@ -1,4 +1,4 @@ -
+

کد استار

@@ -46,41 +46,42 @@

کد استار

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -108,7 +109,6 @@

کد استار

@@ -128,4 +128,4 @@

کد استار

- + diff --git a/project/src/app/components/login/login.component.scss b/project/src/app/components/login/login.component.scss index f07158f..a97b926 100644 --- a/project/src/app/components/login/login.component.scss +++ b/project/src/app/components/login/login.component.scss @@ -8,9 +8,9 @@ @apply w-[100%] h-[100%] hidden lg:block } .input-container { - @apply flex flex-row justify-start items-center w-[24rem] h-[2.7rem] border-b-[0.1rem] gap-[0.5rem] mt-[2rem] relative; - color: black; - border-bottom-color: black; + @apply flex flex-row justify-start items-center w-[40rem] h-[2.7rem] border-b-[0.1rem] gap-[0.5rem] mt-[2rem] relative; + color: $secondary-color; + border-bottom-color: $secondary-color; &:focus-within { color: $bg-color; @@ -29,16 +29,16 @@ } label { - @apply absolute text-[1.5rem] transition-all duration-300 ease-in-out; + @apply absolute text-[2rem] transition-all duration-300 ease-in-out; color: inherit; - inset-inline-start: 2rem; - inset-block-end: 0.3rem; + inset-inline-start: 2.8rem; + inset-block-end: 0.4rem; } input:focus + label, input:not(:placeholder-shown) + label { - @apply text-[1rem] transition-all duration-300 ease-in-out; - inset-block-start: -1.5rem; + @apply text-[1.4rem] transition-all duration-300 ease-in-out; + inset-block-start: -2rem; inset-inline-start: 0.4rem; } } @@ -94,7 +94,7 @@ } } .form-button { - @apply flex flex-row gap-2 self-stretch justify-center rounded-full p-2 mt-[4rem] transition-all duration-300 delay-75 ease-in-out text-[1.5rem] items-center; + @apply flex w-full flex-row gap-2 justify-center rounded-full mt-[4rem] transition-all duration-300 delay-75 ease-in-out text-[1.8rem] items-center; background: $accent-color; color: $bg-color; @@ -102,13 +102,20 @@ border: 1px solid $accent-color; &:hover { - @apply transition-all duration-300 delay-75 ease-in-out; + @apply transition-all duration-300 delay-75 ease-in-out; - background: $success-color; + background: $secondary-color; + border: 1px solid $secondary-color; + } + + &:active { + @apply transition-transform duration-150 ease-in-out; - border: 1px solid $success-color; + transform: scale(0.95); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); } - } +} + .rectangle-top-left-lg { @apply absolute top-0 left-0 w-[9.3rem] h-[25.6rem] fill-none; } @@ -122,21 +129,24 @@ @apply absolute w-[29rem] h-[7.2rem] fill-none left-[17.6rem] bottom-0; } .container-lg { - @apply flex flex-row-reverse gap-0 w-[100rem] h-[50rem]; + @apply flex flex-row-reverse gap-0 w-[120rem] h-[60rem]; + } + .p-svg-form-lg { + @apply w-[100%] h-[100%] fill-none basis-2/5 rounded-l-3xl flex w-full ; + background-color: $secondary-color; } .svg-form-lg { - @apply w-[100%] h-[100%] fill-none basis-1/2 rounded-l-3xl; - background-color: rgb(18 91 154 / 80%); + @apply mx-[-30px] w-[31vw]; } .form-container-lg { - @apply basis-1/2 rounded-r-3xl flex flex-row justify-center items-center; + @apply basis-3/5 rounded-r-3xl flex flex-row justify-center items-center; background-color: $primary-color; } .form-lg { - @apply flex flex-col justify-start items-center gap-[1rem]; + @apply flex flex-col justify-start items-center gap-[2rem]; ng-icon { - @apply w-[1.5rem] h-[1.5rem]; + @apply w-[1.8rem] h-[1.8rem]; } } .form-title-lg { diff --git a/project/src/styles.scss b/project/src/styles.scss index bccfae0..6fa587e 100644 --- a/project/src/styles.scss +++ b/project/src/styles.scss @@ -2,25 +2,35 @@ @tailwind components; @tailwind utilities; +@font-face { + font-family: "VazirmatnBold"; + src: url('./app/statics/Vazirmatn/static/Vazirmatn-Bold.ttf'); +} @font-face { font-family: "Vazirmatn"; - src: url('./app/statics/Vazirmatn/static/Vazirmatn-Black.ttf'); + src: url('./app/statics/Vazirmatn/static/Vazirmatn-Medium.ttf'); +} +@font-face { + font-family: "VazirMatnLight"; + src: url('./app/statics/Vazirmatn/static/Vazirmatn-Light.ttf'); } + *, *::before, *::after { box-sizing: border-box; - margin: 0; padding: 0; } -html { +html{ font-family: "Vazirmatn", sans-serif; - font-size: 62.5%; } +html, body , h1,h2,h3,h4,h5,h6,p,li,span,a ,label,input, ::placeholder ,button{ + font-family: "Vazirmatn" , sans-serif !important ; +} input, label, diff --git a/project/src/variables.scss b/project/src/variables.scss index 4bd7cdd..c5ee770 100644 --- a/project/src/variables.scss +++ b/project/src/variables.scss @@ -5,7 +5,10 @@ // Colors $bg-color: #FDFDFD; -$primary-color: rgb(18, 91, 154); + +$primary-color: #002B5B; +$text-color : #172535; +$secondary-color: #4F86C9; $success-color: #4FBD70; $accent-color: #E9B91B; $error-color: #F05A7E; @@ -15,7 +18,6 @@ $error-color: #F05A7E; .fullscreen { @apply flex flex-row justify-center items-center min-h-screen relative; - background-color: $bg-color; inline-size: 100%; } From 1e5f80b0cfba3f8223f9acdb1a22c4586ccbe903 Mon Sep 17 00:00:00 2001 From: parsa Date: Mon, 26 Aug 2024 05:10:28 +0330 Subject: [PATCH 06/12] fix: fix a bug --- project/src/app/components/login/login.component.html | 2 +- project/src/app/components/login/login.component.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/project/src/app/components/login/login.component.html b/project/src/app/components/login/login.component.html index fc62951..ef676b0 100644 --- a/project/src/app/components/login/login.component.html +++ b/project/src/app/components/login/login.component.html @@ -128,4 +128,4 @@

کد استار

- + diff --git a/project/src/app/components/login/login.component.ts b/project/src/app/components/login/login.component.ts index 492c606..7c1c7ec 100644 --- a/project/src/app/components/login/login.component.ts +++ b/project/src/app/components/login/login.component.ts @@ -4,11 +4,12 @@ import {UserService} from "../../services/user/user.service"; import {NgIconComponent, provideIcons} from "@ng-icons/core"; import { heroUser, heroLockClosed, heroArrowLeftEndOnRectangle } from '@ng-icons/heroicons/outline'; import {Router} from "@angular/router"; +import { BgGifComponent } from '../bg-gif/bg-gif.component'; @Component({ selector: 'app-login', standalone: true, - imports: [ReactiveFormsModule, NgIconComponent], + imports: [ReactiveFormsModule, NgIconComponent ,BgGifComponent], templateUrl: './login.component.html', styleUrl: './login.component.scss', providers: [provideIcons({heroUser, heroLockClosed, heroArrowLeftEndOnRectangle})] From 5c99706722b93567d7cd1816a2f2ea1b3bfc34af Mon Sep 17 00:00:00 2001 From: FrOZEn-FurY Date: Mon, 26 Aug 2024 16:15:04 +0330 Subject: [PATCH 07/12] Fixed somethings --- .../dashboard/show-data/show-data.component.html | 2 ++ .../components/dashboard/show-data/show-data.component.ts | 2 +- project/src/app/components/login/login.component.html | 1 + project/src/app/components/login/login.component.scss | 8 ++++---- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/project/src/app/components/dashboard/show-data/show-data.component.html b/project/src/app/components/dashboard/show-data/show-data.component.html index 804f004..40d3d1d 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.html +++ b/project/src/app/components/dashboard/show-data/show-data.component.html @@ -41,3 +41,5 @@

هیچ داده ای برای نمایش یافت نشد.

+ +
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.ts b/project/src/app/components/dashboard/show-data/show-data.component.ts index 4f68c9a..f87462f 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.ts +++ b/project/src/app/components/dashboard/show-data/show-data.component.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, EventEmitter, HostListener, Output, ViewChild} from '@angular/core'; +import {Component, ElementRef, ViewChild} from '@angular/core'; import {UserService} from "../../../services/user/user.service"; import User from "../../../interfaces/user"; import {FormsModule} from "@angular/forms"; diff --git a/project/src/app/components/login/login.component.html b/project/src/app/components/login/login.component.html index ef676b0..b3323ee 100644 --- a/project/src/app/components/login/login.component.html +++ b/project/src/app/components/login/login.component.html @@ -109,6 +109,7 @@

کد استار

diff --git a/project/src/app/components/login/login.component.scss b/project/src/app/components/login/login.component.scss index a97b926..5b9c79e 100644 --- a/project/src/app/components/login/login.component.scss +++ b/project/src/app/components/login/login.component.scss @@ -59,7 +59,7 @@ } .form-title { @apply mb-[4rem] text-[3rem] font-bold; - color: $primary-color; + color: $bg-color; } .form-identifier { @apply input-container; @@ -111,8 +111,8 @@ &:active { @apply transition-transform duration-150 ease-in-out; - transform: scale(0.95); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + transform: scale(0.95); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); } } @@ -132,7 +132,7 @@ @apply flex flex-row-reverse gap-0 w-[120rem] h-[60rem]; } .p-svg-form-lg { - @apply w-[100%] h-[100%] fill-none basis-2/5 rounded-l-3xl flex w-full ; + @apply h-[100%] fill-none basis-2/5 rounded-l-3xl flex w-full ; background-color: $secondary-color; } .svg-form-lg { From 00a815defa34ca15003a40ab00126ba6a798cb8b Mon Sep 17 00:00:00 2001 From: parsa Date: Tue, 27 Aug 2024 03:51:53 +0330 Subject: [PATCH 08/12] feat: add home page to dashbord , chenge a design of dashbord nav , add a eye for login password --- project/src/app/app.routes.ts | 2 + .../change-password.component.ts | 2 +- .../dashboard/dashboard.component.html | 12 ++- .../dashboard/dashboard.component.scss | 33 +++++---- .../dashboard/dashboard.component.ts | 15 +++- .../dashboardhome.component.html | 6 ++ .../dashboardhome.component.scss | 3 + .../dashboardhome.component.spec.ts | 23 ++++++ .../dashboardhome/dashboardhome.component.ts | 12 +++ .../edit-profile/edit-profile.component.ts | 2 +- .../manage-users/manage-users.component.ts | 2 +- .../dashboard/navbar/navbar.component.html | 52 +++++++++++-- .../dashboard/navbar/navbar.component.scss | 73 ++++++++++++++++--- .../dashboard/navbar/navbar.component.ts | 9 ++- .../app/components/login/login.component.html | 9 ++- .../app/components/login/login.component.ts | 15 +++- .../src/app/guards/admin/is-admin.guard.ts | 2 +- .../app/guards/loggedIn/logged-in.guard.ts | 2 +- project/src/variables.scss | 2 +- 19 files changed, 225 insertions(+), 51 deletions(-) create mode 100644 project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html create mode 100644 project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss create mode 100644 project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts create mode 100644 project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts diff --git a/project/src/app/app.routes.ts b/project/src/app/app.routes.ts index 3a583b1..1884aec 100644 --- a/project/src/app/app.routes.ts +++ b/project/src/app/app.routes.ts @@ -8,9 +8,11 @@ import {ChangePasswordComponent} from "./components/dashboard/change-password/ch import {ManageUsersComponent} from "./components/dashboard/manage-users/manage-users.component"; import {ShowDataComponent} from "./components/dashboard/show-data/show-data.component"; import {isAdminGuard} from "./guards/admin/is-admin.guard"; +import { DashboardhomeComponent } from './components/dashboard/dashboardhome/dashboardhome.component'; export const routes: Routes = [ {path: 'dashboard', component: DashboardComponent, children: [ + {path: 'home', component: DashboardhomeComponent}, {path: 'profile', component: ProfileComponent}, {path: 'edit-profile', component: EditProfileComponent}, {path: 'change-password', component: ChangePasswordComponent}, diff --git a/project/src/app/components/dashboard/change-password/change-password.component.ts b/project/src/app/components/dashboard/change-password/change-password.component.ts index 8ad747c..c984948 100644 --- a/project/src/app/components/dashboard/change-password/change-password.component.ts +++ b/project/src/app/components/dashboard/change-password/change-password.component.ts @@ -32,7 +32,7 @@ export class ChangePasswordComponent { newPassword: this.formGroup.value.newPassword } this.modifyService.changePassword(data); - this.router.navigate(['dashboard/profile']) + this.router.navigate(['dashboard/home']) } else { alert("مشکلی در ورودی های شما وجود دارد.") } diff --git a/project/src/app/components/dashboard/dashboard.component.html b/project/src/app/components/dashboard/dashboard.component.html index bb92da4..3bfd247 100644 --- a/project/src/app/components/dashboard/dashboard.component.html +++ b/project/src/app/components/dashboard/dashboard.component.html @@ -1,12 +1,16 @@ -
-
+
+ + + +
-
+
+ diff --git a/project/src/app/components/dashboard/dashboard.component.scss b/project/src/app/components/dashboard/dashboard.component.scss index eb90c81..8578725 100644 --- a/project/src/app/components/dashboard/dashboard.component.scss +++ b/project/src/app/components/dashboard/dashboard.component.scss @@ -1,21 +1,24 @@ @import "../../../variables"; @layer components { - .dashboard-navbar-icon { - @apply h-[3rem] w-[3rem] hover:cursor-pointer absolute top-1 right-0 rounded-2xl; - background-color: $primary-color; - color: $bg-color; - } - .dashboard-navbar-back-icon { - @apply h-[3rem] w-[3rem] hover:cursor-pointer; - } - .dashboard-navbar { - @apply self-stretch w-[100vw] lg:w-[20vw] p-4; - background-color: $primary-color; - color: $bg-color; - } - .dashboard-content { - @apply self-stretch flex w-[99vw] flex-row justify-center items-center; + // .dashboard-navbar-icon { + // @apply h-[3rem] w-[3rem] hover:cursor-pointer absolute top-1 right-0 rounded-2xl; + // background-color: $primary-color; + // color: $bg-color; + // } + // .dashboard-navbar-back-icon { + // @apply h-[3rem] w-[3rem] hover:cursor-pointer; + // } + // .dashboard-navbar { + // @apply self-stretch w-[100vw] lg:w-[20vw] p-4; + // background-color: $primary-color; + // color: $bg-color; + // } + // .dashboard-content { + // @apply self-stretch flex w-[80vw] flex-row justify-center items-center; + // background-color: $bg-color; + // } + .fullscreen{ background-color: $bg-color; } } diff --git a/project/src/app/components/dashboard/dashboard.component.ts b/project/src/app/components/dashboard/dashboard.component.ts index 65e2a0e..196963f 100644 --- a/project/src/app/components/dashboard/dashboard.component.ts +++ b/project/src/app/components/dashboard/dashboard.component.ts @@ -3,7 +3,8 @@ import {NavbarComponent} from "./navbar/navbar.component"; import {RouterOutlet} from "@angular/router"; import {NgIconComponent, provideIcons} from "@ng-icons/core"; import { heroBars3, heroArrowUturnRight } from '@ng-icons/heroicons/outline' -import {isPlatformBrowser} from "@angular/common"; +import {isPlatformBrowser, NgClass} from "@angular/common"; +import { BgGifComponent } from "../bg-gif/bg-gif.component"; @Component({ selector: 'app-dashboard', @@ -11,8 +12,10 @@ import {isPlatformBrowser} from "@angular/common"; imports: [ NavbarComponent, RouterOutlet, - NgIconComponent - ], + NgIconComponent, + NgClass, + BgGifComponent +], providers: [provideIcons({heroBars3, heroArrowUturnRight})], templateUrl: './dashboard.component.html', styleUrl: './dashboard.component.scss' @@ -72,4 +75,10 @@ constructor(private renderer: Renderer2, @Inject(PLATFORM_ID) private platform: this.renderer.setStyle(this.contentRef.nativeElement, 'inline-size', '99vw'); this.renderer.setStyle(this.contentRef.nativeElement, 'display', 'flex'); } + + isMenuOpen = false; + + toggleMenu() { + this.isMenuOpen = !this.isMenuOpen; + } } diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html new file mode 100644 index 0000000..eee6284 --- /dev/null +++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.html @@ -0,0 +1,6 @@ +
+ + +

به کد استار خوش امدید

+ +
\ No newline at end of file diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss new file mode 100644 index 0000000..a591c63 --- /dev/null +++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.scss @@ -0,0 +1,3 @@ +.VazirmatnBold{ + font-family: "VazirmatnBold" !important; +} \ No newline at end of file diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts new file mode 100644 index 0000000..e32b785 --- /dev/null +++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardhomeComponent } from './dashboardhome.component'; + +describe('DashboardhomeComponent', () => { + let component: DashboardhomeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DashboardhomeComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DashboardhomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts new file mode 100644 index 0000000..918d651 --- /dev/null +++ b/project/src/app/components/dashboard/dashboardhome/dashboardhome.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-dashboardhome', + standalone: true, + imports: [], + templateUrl: './dashboardhome.component.html', + styleUrl: './dashboardhome.component.scss' +}) +export class DashboardhomeComponent { + +} diff --git a/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts b/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts index 2faa6f3..ba2b8a6 100644 --- a/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts +++ b/project/src/app/components/dashboard/edit-profile/edit-profile.component.ts @@ -36,7 +36,7 @@ export class EditProfileComponent { userName: this.formGroup.value.userName || this.userService.getUser()?.userName } this.modifyService.modifyUser(data); - this.router.navigate(['dashboard/profile']); + this.router.navigate(['dashboard/home']); } else { alert("اشکالی در ورودی های شما وجود دارد.") } diff --git a/project/src/app/components/dashboard/manage-users/manage-users.component.ts b/project/src/app/components/dashboard/manage-users/manage-users.component.ts index dbe1b9e..6953141 100644 --- a/project/src/app/components/dashboard/manage-users/manage-users.component.ts +++ b/project/src/app/components/dashboard/manage-users/manage-users.component.ts @@ -59,7 +59,7 @@ export class ManageUsersComponent { if (user?.userName === this.finalUsers[index].userName!) { user.role = selected; this.userService.setUser(user); - this.router.navigateByUrl('dashboard/profile'); + this.router.navigateByUrl('dashboard/home'); } } } diff --git a/project/src/app/components/dashboard/navbar/navbar.component.html b/project/src/app/components/dashboard/navbar/navbar.component.html index 10f8dd4..bb927b2 100644 --- a/project/src/app/components/dashboard/navbar/navbar.component.html +++ b/project/src/app/components/dashboard/navbar/navbar.component.html @@ -1,10 +1,50 @@ diff --git a/project/src/app/components/dashboard/navbar/navbar.component.scss b/project/src/app/components/dashboard/navbar/navbar.component.scss index 529255e..7336cca 100644 --- a/project/src/app/components/dashboard/navbar/navbar.component.scss +++ b/project/src/app/components/dashboard/navbar/navbar.component.scss @@ -1,23 +1,76 @@ @import '../../../../variables'; @layer components { + .navbar-list { - @apply flex flex-col justify-start p-4 gap-2; + @apply flex flex-col justify-start gap-3 p-4 w-full self-center ; + background-color: $primary-color; + height: 90%; + border-radius: 20px 0 10px 20px; + + .logo{ + height: 160px; + margin-bottom: 40px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 15px; + p{ + color: $bg-color; + font-size: 32px; + } + svg{ + height: 50px; + fill: #fdfdfd; + width: 50px; + padding: 5px; + } } +} .navbar-item { - @apply text-[2rem] hover:cursor-pointer border-b-2 p-4 transition-all duration-300 delay-75 ease-in-out; - border-bottom-color: $bg-color; + @apply text-[2rem] hover:cursor-pointer transition-all duration-100 delay-75 ease-in-out rounded-2xl; + color: $secondary-color; - &:hover { - @apply transition-all duration-300 delay-75 ease-in-out; + background-color: $primary-color; + border: 3px solid $primary-color; + height: 65px; + display: grid; + grid-template-columns: 1fr 5fr 1fr; + align-items: center; + gap: 10px; + transition-duration: 0.3s; - color: $accent-color; - border-bottom-color: $accent-color; + padding-right: 10px; + &:hover { + // @apply transition-all duration-300 delay-75 ease-in-out; + color: $secondary-color; + // background-color: $secondary-color; + border: 3px solid $secondary-color; + .heroArrowLeft{ + color: $secondary-color; + } + } + ng-icon{ + --ng-icon__size: 2.3rem !important; + } + .heroArrowLeft{ + transition-duration: 0.3s; + color: $primary-color; } } .active { - background: $bg-color; - color: black; - border-bottom-color: black; + background: $secondary-color; + color: $bg-color; + + &:hover { + color: $bg-color + } + + .heroArrowLeft{ + transition-duration: 0.5s; + color: $secondary-color; + } } + } diff --git a/project/src/app/components/dashboard/navbar/navbar.component.ts b/project/src/app/components/dashboard/navbar/navbar.component.ts index c069e76..d310538 100644 --- a/project/src/app/components/dashboard/navbar/navbar.component.ts +++ b/project/src/app/components/dashboard/navbar/navbar.component.ts @@ -2,16 +2,21 @@ import { Component } from '@angular/core'; import {RouterLink, RouterLinkActive} from "@angular/router"; import {UserService} from "../../../services/user/user.service"; import User from "../../../interfaces/user"; +import { NgIconComponent, provideIcons } from '@ng-icons/core'; +import { heroArrowLeft, heroHome, heroUser } from '@ng-icons/heroicons/outline'; @Component({ selector: 'app-navbar', standalone: true, imports: [ RouterLink, - RouterLinkActive + RouterLinkActive, + NgIconComponent ], templateUrl: './navbar.component.html', - styleUrl: './navbar.component.scss' + styleUrl: './navbar.component.scss', + providers: [provideIcons({heroHome,heroUser,heroArrowLeft})] + }) export class NavbarComponent { user!: User | undefined; diff --git a/project/src/app/components/login/login.component.html b/project/src/app/components/login/login.component.html index ef676b0..3c9a810 100644 --- a/project/src/app/components/login/login.component.html +++ b/project/src/app/components/login/login.component.html @@ -17,7 +17,7 @@

کد استار

@@ -102,11 +102,14 @@

کد استار

+ /> + + +

+
+ + +
@@ -40,6 +44,11 @@

هیچ داده ای برای نمایش یافت نشد.

+
- -
+
+
    +
  • نمایش کاربر
  • +
  • افزایش تراکنش ها
  • +
+
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.scss b/project/src/app/components/dashboard/show-data/show-data.component.scss index 1228ed5..7559198 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.scss +++ b/project/src/app/components/dashboard/show-data/show-data.component.scss @@ -87,4 +87,48 @@ @apply absolute w-[4rem] h-[4rem] top-0 right-0; color: $bg-color; } + .form-search-user { + @apply text-[2rem] self-center border rounded-2xl transition-all duration-300 ease-in-out p-4 flex flex-row + gap-4 items-center; + inline-size: 70rem; + + input { + @apply flex-grow p-4 rounded-2xl; + } + } + #graph-container { + @apply self-stretch flex-grow m-8 rounded-xl; + background-color: $secondary-color; + } +} +.context-menu-container { + position: absolute; + + display: none; + + padding: 1rem; + + flex-direction: column; + justify-content: center; + align-items: center; + + background-color: $primary-color; + color: $bg-color; + + font-size: 1.6rem; + + ul { + list-style: none; + + padding: 0.1rem; + + li { + padding: 0.5rem; + user-select: none; + + &:hover { + cursor: pointer; + } + } + } } diff --git a/project/src/app/components/dashboard/show-data/show-data.component.ts b/project/src/app/components/dashboard/show-data/show-data.component.ts index f87462f..7e8f600 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.ts +++ b/project/src/app/components/dashboard/show-data/show-data.component.ts @@ -1,4 +1,4 @@ -import {Component, ElementRef, ViewChild} from '@angular/core'; +import {Component, ElementRef, EventEmitter, HostListener, Output, ViewChild} from '@angular/core'; import {UserService} from "../../../services/user/user.service"; import User from "../../../interfaces/user"; import {FormsModule} from "@angular/forms"; @@ -9,6 +9,8 @@ import {PersianDatePipe} from "./pipes/persian-date.pipe"; import {heroXMark} from "@ng-icons/heroicons/outline"; import {NgIconComponent, provideIcons} from "@ng-icons/core"; import {BlurClickDirective} from "../../../directives/blur-click.directive"; +import * as d3 from 'd3'; +import {FetchDataService} from "../../../services/fetchData/fetch-data.service"; interface Transaction { TransactionId: number, @@ -19,6 +21,24 @@ interface Transaction { type: string } +interface Node { + x: number; + y: number; + vx: number; + vy: number; + fx?: number | null; + fy?: number | null; + label: string | number; +} + +interface Link { + source: Node; + target: Node; + date: string; + amount: string; + type: string; +} + @Component({ selector: 'app-show-data', standalone: true, @@ -36,14 +56,19 @@ interface Transaction { export class ShowDataComponent { user!: User | undefined; data: Transaction[] | undefined = undefined; - dataGot = false; + nodes: Node[] = []; + links: Link[] = []; + + @Output() dataGotEvent = new EventEmitter(); @ViewChild('labelElement') labelElement!: ElementRef; @ViewChild('inputElement') inputElement!: ElementRef; @ViewChild('selectElement') selectElement!: ElementRef; @ViewChild('dataElement') dataElement!: ElementRef; + @ViewChild('graphElement') graphElement!: ElementRef; + @ViewChild('contextElement') contextElement!: ElementRef; - constructor(private userService: UserService, private http: HttpClient) { + constructor(private userService: UserService, private http: HttpClient, private fetchDataService: FetchDataService) { this.user = this.userService.getUser(); } @@ -64,30 +89,18 @@ export class ShowDataComponent { const file = this.inputElement.nativeElement.files[0]; formData.append('file', file); if (this.selectElement.nativeElement.value === "transaction") { - this.http.post(API_BASE_URL + 'Transaction/ImportTransactions', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { + this.http.post(API_BASE_URL + 'transactions/upload', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { console.log(response); }) } else if (this.selectElement.nativeElement.value === "account") { - this.http.post(API_BASE_URL + 'Account/ImportAccounts', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { + this.http.post(API_BASE_URL + 'accounts/upload', formData, {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { console.log(response); }) } } } - updateData(): void { - this.dataGot = false; - const token: string | null = this.getToken(); - this.http.get(API_BASE_URL + 'Transaction/GetAllTransactions', {headers: {"Authorization": "Bearer " + token}}).subscribe((response) => { - this.data = response; - this.dataGot = true; - }) - } - showData(): void { - if (this.data === undefined) { - this.updateData(); - } this.dataElement.nativeElement.style.display = 'flex'; } @@ -103,4 +116,194 @@ export class ShowDataComponent { return token; } + + async ngOnInit() { + const response = await this.fetchDataService.fetchData(); + this.data = response; + for (const trans of response) { + if (!this.nodes.find(node => node.label === trans.sourceAccountId)) { + this.nodes.push({ + x: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].x + 1 : 1, + y: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].y + 1 : 1, + vx: 1, + vy: 1, + label: trans.sourceAccountId, + }); + } + if (!this.nodes.find(node => node.label === trans.destinationAccountId)) { + this.nodes.push({ + x: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].x + 1 : 1, + y: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].y + 1 : 1, + vx: 1, + vy: 1, + label: trans.destinationAccountId, + }); + } + this.links.push({ + source: this.nodes.find(node => node.label === trans.sourceAccountId)!, + target: this.nodes.find(node => node.label === trans.destinationAccountId)!, + date: (new PersianDatePipe()).transform(trans.date), + type: trans.type, + amount: (new RialPipePipe()).transform(trans.amount), + }); + } + this.dataGotEvent.emit(); + } + + @HostListener('dataGotEvent') + handleGraph(): void { + const element = d3.select(this.graphElement.nativeElement) + .append('svg') + .attr('width', this.graphElement.nativeElement.clientWidth) + .attr('height', this.graphElement.nativeElement.clientHeight); + + + const svgGroup = element.append('g'); + + const zoom = d3.zoom() + .scaleExtent([0.5, 4]) + .on('zoom', (event) => { + svgGroup.attr('transform', event.transform); + }); + + element.call(zoom); + + const simulation = d3.forceSimulation(this.nodes) + .force("link", d3.forceLink(this.links)); + + const link = svgGroup.append('g') + .attr('class', 'links') + .selectAll('line') + .data(this.links) + .enter() + .append('line') + .attr('stroke-width', 2) + .attr('stroke', '#FDFDFD'); + + + const linkLabelsAmount = svgGroup.append('g') + .attr('class', 'link-labels') + .selectAll('text') + .data(this.links) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('fill', '#172535') + .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') + .text((d: Link) => d.amount ? d.amount : ""); + + const linkLabelsType = svgGroup.append('g') + .attr('class', 'link-labels') + .selectAll('text') + .data(this.links) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('fill', '#172535') + .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') + .text((d: Link) => d.type ? d.type : ""); + + const linkLabelsDate = svgGroup.append('g') + .attr('class', 'link-labels') + .selectAll('text') + .data(this.links) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('fill', '#172535') + .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') + .text((d: Link) => d.date ? d.date : ""); + + const node = svgGroup.append('g') + .attr('class', 'nodes') + .selectAll('circle') + .data(this.nodes) + .enter() + .append('circle') + .attr('r', 10) + .attr('fill', '#002B5B') + .call(d3.drag() + .on('start', dragStarted) + .on('drag', dragged) + .on('end', dragEnded) + ); + + node.on('contextmenu', (event: MouseEvent, d: Node) => { + event.preventDefault(); + + this.contextElement.nativeElement.style.display = 'flex'; + this.contextElement.nativeElement.style.top = event.clientY + 'px'; + this.contextElement.nativeElement.style.left = event.clientX + 'px'; + }); + + const nodeLabels = svgGroup.append('g') + .attr('class', 'node-labels') + .selectAll('text') + .data(this.nodes) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('dy', -10) // Position above the node + .attr('fill', '#172535') + .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') + .text((d: Node) => d.label ? d.label : ""); + + function ticked() { + link + .attr('x1', d => d.source.x) + .attr('y1', d => d.source.y) + .attr('x2', d => d.target.x) + .attr('y2', d => d.target.y); + + node + .attr('cx', d => d.x) + .attr('cy', d => d.y); + + // Update positions of node labels + nodeLabels + .attr('x', d => d.x) + .attr('y', d => d.y); + + // Update positions of link labels + linkLabelsAmount + .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2) + .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2); + + linkLabelsDate + .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2) + .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2 + 20); + + linkLabelsType + .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2) + .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2 + 40); + } + + simulation.on('tick', ticked); + + simulation + .force('link', d3.forceLink(this.links).id((d, i) => i).distance(250)) + .force('charge', d3.forceManyBody().strength(-350)) + .force('center', d3.forceCenter(this.graphElement.nativeElement.clientWidth / 2, this.graphElement.nativeElement.clientHeight / 2)); + + function dragStarted(event: d3.D3DragEvent, d: Node) { + if (!event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(event: d3.D3DragEvent, d: Node) { + d.fx = event.x; + d.fy = event.y; + } + + function dragEnded(event: d3.D3DragEvent, d: Node) { + if (!event.active) simulation.alphaTarget(0); + d.fx = null; + d.fy = null; + } + } + + handleCloseContext() { + this.contextElement.nativeElement.style.display = 'none'; + } } diff --git a/project/src/app/services/fetchData/fetch-data.service.spec.ts b/project/src/app/services/fetchData/fetch-data.service.spec.ts new file mode 100644 index 0000000..5bcbb9e --- /dev/null +++ b/project/src/app/services/fetchData/fetch-data.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { FetchDataService } from './fetch-data.service'; + +describe('FetchDataService', () => { + let service: FetchDataService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(FetchDataService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/project/src/app/services/fetchData/fetch-data.service.ts b/project/src/app/services/fetchData/fetch-data.service.ts new file mode 100644 index 0000000..09b0403 --- /dev/null +++ b/project/src/app/services/fetchData/fetch-data.service.ts @@ -0,0 +1,39 @@ +import {Inject, Injectable, PLATFORM_ID} from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {isPlatformBrowser} from "@angular/common"; +import {API_BASE_URL} from "../../app.config"; +import {firstValueFrom} from "rxjs"; + +interface Transaction { + TransactionId: number, + sourceAccountId: number, + destinationAccountId: number, + amount: number, + date: string, + type: string +} + +@Injectable({ + providedIn: 'root' +}) +export class FetchDataService { + + constructor(private http: HttpClient, @Inject(PLATFORM_ID) private platform: object) { } + + fetchData(): Promise { + const token = this.getToken(); + return firstValueFrom(this.http.get(API_BASE_URL + 'transactions', {headers: {'Authorization': "Bearer " + token}})) + } + + getToken(): string | null { + if(isPlatformBrowser(this.platform)) { + let token = localStorage.getItem("token"); + if (token) { + token = token.substring(1, token.length - 1); + } + + return token; + } + return null; + } +} diff --git a/project/src/app/services/modify-user/modify-user.service.ts b/project/src/app/services/modify-user/modify-user.service.ts index 723aec7..959713b 100644 --- a/project/src/app/services/modify-user/modify-user.service.ts +++ b/project/src/app/services/modify-user/modify-user.service.ts @@ -31,7 +31,7 @@ export class ModifyUserService { modifyUser(data: ChangeData): void { const token = this.getToken(); - this.http.put(API_BASE_URL + 'Profile/EditProfileInfo', data, {headers: {'Authorization': "Bearer " + token}}) + this.http.put(API_BASE_URL + 'profile/edit-info', data, {headers: {'Authorization': "Bearer " + token}}) .subscribe(() => { const user = { firstName: data.firstName, @@ -45,7 +45,7 @@ export class ModifyUserService { changePassword(data: ChangePassword): void { const token = this.getToken(); - this.http.put(API_BASE_URL + 'Profile/ChangePassword', data, {headers: {'Authorization': "Bearer " + token}}) + this.http.put(API_BASE_URL + 'profile/change-password', data, {headers: {'Authorization': "Bearer " + token}}) .subscribe(response => { console.log(response); }); @@ -58,7 +58,7 @@ export class ModifyUserService { role } - this.http.put(API_BASE_URL + 'Identity/ChangeRole', data, {headers: {'Authorization': "Bearer " + token}}) + this.http.patch(API_BASE_URL + 'identity/change-role', data, {headers: {'Authorization': "Bearer " + token}}) .subscribe((response) => { console.log(response); }) diff --git a/project/src/app/services/user/user.service.ts b/project/src/app/services/user/user.service.ts index 0fba0bd..8fbbc83 100644 --- a/project/src/app/services/user/user.service.ts +++ b/project/src/app/services/user/user.service.ts @@ -36,7 +36,7 @@ export class UserService { username: user.identifier.includes('@') ? null : user.identifier, password: user.password, } - this.http.post(`${API_BASE_URL}Identity/Login`, obj).subscribe((res: any) => { + this.http.post(`${API_BASE_URL}identity/login`, obj).subscribe((res: any) => { this.user = {}; this.user.userName = res?.username; localStorage.setItem('token', JSON.stringify(res?.token)); From c557ffa01fd2c1ecf008c0b27795168661aab1a8 Mon Sep 17 00:00:00 2001 From: FrOZEn-FurY Date: Thu, 29 Aug 2024 01:28:00 +0330 Subject: [PATCH 12/12] feat: Showing the graph correctly completed --- .../show-data/show-data.component.html | 3 +- .../show-data/show-data.component.scss | 2 +- .../show-data/show-data.component.ts | 151 +++++++++++++----- .../services/fetchData/fetch-data.service.ts | 17 ++ 4 files changed, 128 insertions(+), 45 deletions(-) diff --git a/project/src/app/components/dashboard/show-data/show-data.component.html b/project/src/app/components/dashboard/show-data/show-data.component.html index f787d4a..e7a003d 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.html +++ b/project/src/app/components/dashboard/show-data/show-data.component.html @@ -15,7 +15,8 @@
- + +
diff --git a/project/src/app/components/dashboard/show-data/show-data.component.scss b/project/src/app/components/dashboard/show-data/show-data.component.scss index 7559198..2b0fc75 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.scss +++ b/project/src/app/components/dashboard/show-data/show-data.component.scss @@ -97,7 +97,7 @@ } } #graph-container { - @apply self-stretch flex-grow m-8 rounded-xl; + @apply self-stretch flex-grow m-8 rounded-xl flex flex-row justify-center items-center; background-color: $secondary-color; } } diff --git a/project/src/app/components/dashboard/show-data/show-data.component.ts b/project/src/app/components/dashboard/show-data/show-data.component.ts index 7e8f600..0c60892 100644 --- a/project/src/app/components/dashboard/show-data/show-data.component.ts +++ b/project/src/app/components/dashboard/show-data/show-data.component.ts @@ -59,6 +59,17 @@ export class ShowDataComponent { nodes: Node[] = []; links: Link[] = []; + element!: d3.Selection; + svgGroup!: d3.Selection; + simulation!: d3.Simulation; + link!: d3.Selection; + linkLabelsAmount!: d3.Selection; + linkLabelsDate!: d3.Selection; + linkLabelsType!: d3.Selection; + node!: d3.Selection; + nodeLabels!: d3.Selection; + + @Output() dataGotEvent = new EventEmitter(); @ViewChild('labelElement') labelElement!: ElementRef; @@ -67,6 +78,7 @@ export class ShowDataComponent { @ViewChild('dataElement') dataElement!: ElementRef; @ViewChild('graphElement') graphElement!: ElementRef; @ViewChild('contextElement') contextElement!: ElementRef; + @ViewChild('searchIdElement') searchIdElement!: ElementRef; constructor(private userService: UserService, private http: HttpClient, private fetchDataService: FetchDataService) { this.user = this.userService.getUser(); @@ -82,6 +94,51 @@ export class ShowDataComponent { return !(this?.inputElement?.nativeElement?.files && this?.inputElement?.nativeElement?.files?.length > 0); } + clearGraphTable(): void { + d3.select(this.graphElement.nativeElement).selectAll('*').remove(); + } + + async handleGetUser() { + const response = await this.fetchDataService.fetchDataById(this.searchIdElement.nativeElement.value); + if (response.length === 0) { + this.graphElement.nativeElement.textContent = "داده ای یافت نشد!"; + return; + } + this.nodes = []; + this.links = []; + this.clearGraphTable(); + this.nodes.push({ + x: 1, + y: 1, + vx: 1, + vy: 1, + label: Number(this.searchIdElement.nativeElement.value) + }); + for (const item of response) { + if (!this.nodes.find(node => node.label === item.accountId)) { + this.nodes.push({ + x: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].x + 1 : 1, + y: this.nodes[this.nodes.length - 1] ? this.nodes[this.nodes.length - 1].y + 1 : 1, + vx: 1, + vy: 1, + label: item.accountId, + }); + } + this.links.push({ + source: item.transactionWithSources[0].sourceAcount === item.accountId ? + this.nodes.find(node => node.label === item.accountId)! + : this.nodes.find(node => node.label === Number(this.searchIdElement.nativeElement.value))!, + target: item.transactionWithSources[0].sourceAcount === item.accountId ? + this.nodes.find(node => node.label === Number(this.searchIdElement.nativeElement.value))! + : this.nodes.find(node => node.label === item.accountId)!, + type: item.transactionWithSources[0].type, + amount: (new RialPipePipe()).transform(item.transactionWithSources[0].amount), + date: (new PersianDatePipe()).transform(item.transactionWithSources[0].date), + }) + } + this.dataGotEvent.emit(); + } + onSubmit(): void { const formData: FormData = new FormData(); const token: string | null = this.getToken(); @@ -152,36 +209,51 @@ export class ShowDataComponent { @HostListener('dataGotEvent') handleGraph(): void { - const element = d3.select(this.graphElement.nativeElement) + this.element = d3.select(this.graphElement.nativeElement) .append('svg') .attr('width', this.graphElement.nativeElement.clientWidth) .attr('height', this.graphElement.nativeElement.clientHeight); - const svgGroup = element.append('g'); + this.svgGroup = this.element.append('g'); const zoom = d3.zoom() .scaleExtent([0.5, 4]) .on('zoom', (event) => { - svgGroup.attr('transform', event.transform); + this.svgGroup.attr('transform', event.transform); }); - element.call(zoom); + this.element.call(zoom); - const simulation = d3.forceSimulation(this.nodes) + this.simulation = d3.forceSimulation(this.nodes) .force("link", d3.forceLink(this.links)); - const link = svgGroup.append('g') + this.svgGroup.append('defs').append('marker') + .attr('id', 'arrowhead') + .attr('viewBox', '-0 -5 10 10') + .attr('refX', 13) + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#FDFDFD') + .style('stroke', 'none'); + + this.link = this.svgGroup.append('g') .attr('class', 'links') .selectAll('line') .data(this.links) .enter() .append('line') .attr('stroke-width', 2) - .attr('stroke', '#FDFDFD'); + .attr('stroke', '#FDFDFD') + .attr('marker-end', 'url(#arrowhead)'); - const linkLabelsAmount = svgGroup.append('g') + this.linkLabelsAmount = this.svgGroup.append('g') .attr('class', 'link-labels') .selectAll('text') .data(this.links) @@ -192,7 +264,7 @@ export class ShowDataComponent { .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') .text((d: Link) => d.amount ? d.amount : ""); - const linkLabelsType = svgGroup.append('g') + this.linkLabelsType = this.svgGroup.append('g') .attr('class', 'link-labels') .selectAll('text') .data(this.links) @@ -203,7 +275,7 @@ export class ShowDataComponent { .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') .text((d: Link) => d.type ? d.type : ""); - const linkLabelsDate = svgGroup.append('g') + this.linkLabelsDate = this.svgGroup.append('g') .attr('class', 'link-labels') .selectAll('text') .data(this.links) @@ -214,7 +286,7 @@ export class ShowDataComponent { .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') .text((d: Link) => d.date ? d.date : ""); - const node = svgGroup.append('g') + this.node = this.svgGroup.append('g') .attr('class', 'nodes') .selectAll('circle') .data(this.nodes) @@ -223,12 +295,23 @@ export class ShowDataComponent { .attr('r', 10) .attr('fill', '#002B5B') .call(d3.drag() - .on('start', dragStarted) - .on('drag', dragged) - .on('end', dragEnded) + .on('start', (event: d3.D3DragEvent, d: Node) => { + if (!event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + }) + .on('drag', (event: d3.D3DragEvent, d: Node) => { + d.fx = event.x; + d.fy = event.y; + }) + .on('end', (event: d3.D3DragEvent, d: Node) => { + if (!event.active) this.simulation.alphaTarget(0); + d.fx = null; + d.fy = null; + }) ); - node.on('contextmenu', (event: MouseEvent, d: Node) => { + this.node.on('contextmenu', (event: MouseEvent, d: Node) => { event.preventDefault(); this.contextElement.nativeElement.style.display = 'flex'; @@ -236,7 +319,7 @@ export class ShowDataComponent { this.contextElement.nativeElement.style.left = event.clientX + 'px'; }); - const nodeLabels = svgGroup.append('g') + this.nodeLabels = this.svgGroup.append('g') .attr('class', 'node-labels') .selectAll('text') .data(this.nodes) @@ -248,59 +331,41 @@ export class ShowDataComponent { .attr('style', 'user-select: none;font-weight:bold;font-size:1.5rem;') .text((d: Node) => d.label ? d.label : ""); - function ticked() { - link + this.simulation.on('tick', () => { + this.link .attr('x1', d => d.source.x) .attr('y1', d => d.source.y) .attr('x2', d => d.target.x) .attr('y2', d => d.target.y); - node + this.node .attr('cx', d => d.x) .attr('cy', d => d.y); // Update positions of node labels - nodeLabels + this.nodeLabels .attr('x', d => d.x) .attr('y', d => d.y); // Update positions of link labels - linkLabelsAmount + this.linkLabelsAmount .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2) .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2); - linkLabelsDate + this.linkLabelsDate .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2) .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2 + 20); - linkLabelsType + this.linkLabelsType .attr('x', d => ((d.source as Node).x + (d.target as Node).x) / 2) .attr('y', d => ((d.source as Node).y + (d.target as Node).y) / 2 + 40); - } - - simulation.on('tick', ticked); + }); - simulation + this.simulation .force('link', d3.forceLink(this.links).id((d, i) => i).distance(250)) .force('charge', d3.forceManyBody().strength(-350)) .force('center', d3.forceCenter(this.graphElement.nativeElement.clientWidth / 2, this.graphElement.nativeElement.clientHeight / 2)); - function dragStarted(event: d3.D3DragEvent, d: Node) { - if (!event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - - function dragged(event: d3.D3DragEvent, d: Node) { - d.fx = event.x; - d.fy = event.y; - } - - function dragEnded(event: d3.D3DragEvent, d: Node) { - if (!event.active) simulation.alphaTarget(0); - d.fx = null; - d.fy = null; - } } handleCloseContext() { diff --git a/project/src/app/services/fetchData/fetch-data.service.ts b/project/src/app/services/fetchData/fetch-data.service.ts index 09b0403..f61315c 100644 --- a/project/src/app/services/fetchData/fetch-data.service.ts +++ b/project/src/app/services/fetchData/fetch-data.service.ts @@ -13,6 +13,18 @@ interface Transaction { type: string } +interface AccountTransaction { + transactionWithSources: { + transactionID: number; + sourceAcount: number; + destiantionAccount: number; + amount: number; + date: string; + type: string; + }[]; + accountId: number; +} + @Injectable({ providedIn: 'root' }) @@ -25,6 +37,11 @@ export class FetchDataService { return firstValueFrom(this.http.get(API_BASE_URL + 'transactions', {headers: {'Authorization': "Bearer " + token}})) } + fetchDataById(accountId: string): Promise { + const token = this.getToken(); + return firstValueFrom(this.http.get(API_BASE_URL + 'transactions/by-account/' + accountId, {headers: {'Authorization': "Bearer " + token}})) + } + getToken(): string | null { if(isPlatformBrowser(this.platform)) { let token = localStorage.getItem("token");