Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { Component, inject, input } from '@angular/core';
import { MatCardModule } from '@angular/material/card';
import { MatDialog } from '@angular/material/dialog';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { Category } from '../../../data-model/modules/category/Category';
import { CategorySummaryResponse } from '../../../data-model/modules/category/CategorySummaryResponse';
import { BaseComponent } from '../../../shared/base-component/base.component';
import { ButtonComponent } from '../../../shared/button/button.component';
import { DialogService } from '../../../shared/dialog/dialog.service';
import { DisplaySizeService } from '../../../shared/display-size.service';
import { SnackbarService } from '../../../shared/snackbar/snackbar.service';
import { CreateCategoryDialogComponent } from '../../transactions-and-categories/create-category-dialog/create-category-dialog.component';

// TODO move to interval filter component when created
Expand All @@ -24,26 +22,25 @@ export interface IntervalInfo {

@Component({
selector: 'ex-categories',
imports: [MatProgressBarModule, MatCardModule, ButtonComponent],
imports: [
MatProgressBarModule,
MatCardModule,
ButtonComponent
],
templateUrl: './categories.component.html',
styleUrl: './categories.component.scss',
})
export class CategoriesComponent extends BaseComponent {
public display = inject(DisplaySizeService);
private readonly snackbarService = inject(SnackbarService);
private readonly dialog = inject(MatDialog);
private readonly dialog = inject(DialogService);

totalExpense = input.required<number>();
categories = input.required<CategorySummaryResponse[]>();

openCreateCategoryDialog(): void {
this.dialog.open<CreateCategoryDialogComponent, undefined, Category>(
async openCreateCategoryDialog(): Promise<void> {
await this.dialog.openNonModal(
CreateCategoryDialogComponent, undefined
).afterClosed().subscribe((newCategory) => {
if (newCategory) {
this.snackbarService.showSuccess(`Category '${newCategory.emoji}' created successfully!`);
}
});
);
}

calcPercentage(amount: number): number {
Expand Down
31 changes: 11 additions & 20 deletions frontend/Exence/src/app/private/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { CommonModule } from '@angular/common';
import { Component, computed, inject, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router, RouterModule } from '@angular/router';
import { RouterModule } from '@angular/router';
import { Category } from '../../data-model/modules/category/Category';
import { CategorySummaryResponse } from '../../data-model/modules/category/CategorySummaryResponse';
import { PagedResponse } from '../../data-model/modules/common/PagedResponse';
Expand All @@ -16,13 +15,13 @@ import { ButtonComponent } from '../../shared/button/button.component';
import { CardSliderDirective } from '../../shared/card-slider.directive';
import { ChartComponent } from '../../shared/chart/chart.component';
import { DataTableComponent } from '../../shared/data-table/data-table.component';
import { DialogService } from '../../shared/dialog/dialog.service';
import { DisplaySizeService } from '../../shared/display-size.service';
import { NavigationService } from '../../shared/navigation/navigation.service';
import { SnackbarService } from '../../shared/snackbar/snackbar.service';
import { CurrentUserService } from '../../shared/user/current-user.service';
import { ViewToggleComponent } from '../../shared/view-toggle/view-toggle.component';
import { CategoryService } from '../category.service';
import { CreateTransactionDialogComponent, CreateTransactionDialogData } from '../transactions-and-categories/create-transaction-dialog/create-transaction-dialog.component';
import { CreateTransactionDialogComponent } from '../transactions-and-categories/create-transaction-dialog/create-transaction-dialog.component';
import { TransactionService } from '../transactions-and-categories/transaction.service';

@Component({
Expand All @@ -45,10 +44,8 @@ export class DashboardComponent implements OnInit {
private readonly currentUserService = inject(CurrentUserService);
private readonly transactionService = inject(TransactionService);
private readonly categoryService = inject(CategoryService);
private readonly snackbarService = inject(SnackbarService);
private readonly dialog = inject(DialogService);
readonly display = inject(DisplaySizeService);
readonly dialog = inject(MatDialog);
readonly router = inject(Router);
readonly navigation = inject(NavigationService);


Expand Down Expand Up @@ -93,21 +90,15 @@ export class DashboardComponent implements OnInit {
});
}

public openCreateTransactionDialog(transactionType: TransactionType): void {
const data: CreateTransactionDialogData = {
type: transactionType,
};
this.dialog.open<CreateTransactionDialogComponent, CreateTransactionDialogData, Transaction>(
CreateTransactionDialogComponent, { data }
).afterClosed().subscribe(
async (newTransaction?: Transaction) => {
if (newTransaction) {
this.transactions = (await this.transactionService.list());
this.snackbarService.showSuccess(`Transaction '${newTransaction.title.slice(0, 10)}${newTransaction.title.length > 10 ? '...' : ''}' created successfully!`);
}
});
public async openCreateTransactionDialog(transactionType: TransactionType): Promise<void> {
const result = await this.dialog.openNonModal(
CreateTransactionDialogComponent, { type: transactionType }
);
if (!result) return;
this.transactions = await this.transactionService.list();
}


async onDataChanged(): Promise<void> {
await this.initialize();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Component, inject } from '@angular/core';
import { NonNullableFormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatCardModule } from '@angular/material/card';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
Expand All @@ -10,7 +9,9 @@ import { PickerComponent } from '@ctrl/ngx-emoji-mart';
import { EmojiEvent } from '@ctrl/ngx-emoji-mart/ngx-emoji';
import { Category } from '../../../data-model/modules/category/Category';
import { ButtonComponent } from '../../../shared/button/button.component';
import { DialogComponent } from '../../../shared/dialog/dialog.service';
import { InputClearButtonComponent } from '../../../shared/input-clear-button/input-clear-button.component';
import { SnackbarService } from '../../../shared/snackbar/snackbar.service';
import { ValidatorComponent } from '../../../shared/validator/validator.component';
import { CategoryService } from '../../category.service';

Expand All @@ -31,12 +32,12 @@ import { CategoryService } from '../../category.service';
ValidatorComponent,
],
})
export class CreateCategoryDialogComponent {
private readonly dialogRef = inject(MatDialogRef<CreateCategoryDialogComponent>);
export class CreateCategoryDialogComponent extends DialogComponent<undefined, boolean> {
private readonly categoryService = inject(CategoryService);
private readonly fb = inject(NonNullableFormBuilder);
private readonly snackbarService = inject(SnackbarService);

data = inject(MAT_DIALOG_DATA);
data = this.dialogRef.value;

form = this.fb.group({
name: this.fb.control<string>('', [Validators.required, Validators.maxLength(255)]),
Expand All @@ -58,7 +59,7 @@ export class CreateCategoryDialogComponent {
}

close(): void {
this.dialogRef.close();
this.dialogRef.close(false);
}

async create(): Promise<void> {
Expand All @@ -69,9 +70,10 @@ export class CreateCategoryDialogComponent {
};
try {
const newCategory = await this.categoryService.create(request);
this.dialogRef.close(newCategory);
this.snackbarService.showSuccess(`Category '${newCategory.emoji}' created successfully!`);
this.dialogRef.close(true);
} catch (_err) {
this.dialogRef.close();
this.dialogRef.close(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import { NonNullableFormBuilder, ReactiveFormsModule, Validators } from '@angula
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { Category } from '../../../data-model/modules/category/Category';
import { Transaction } from '../../../data-model/modules/transaction/Transaction';
import { TransactionType } from '../../../data-model/modules/transaction/TransactionType';
import { BaseComponent } from '../../../shared/base-component/base.component';
import { ButtonComponent } from '../../../shared/button/button.component';
import { DialogWithBaseComponent } from '../../../shared/dialog/dialog.service';
import { InputClearButtonComponent } from '../../../shared/input-clear-button/input-clear-button.component';
import { SnackbarService } from '../../../shared/snackbar/snackbar.service';
import { ValidatorComponent } from '../../../shared/validator/validator.component';
import { CategoryService } from '../../category.service';
import { TransactionService } from '../transaction.service';
Expand All @@ -38,13 +38,13 @@ export interface CreateTransactionDialogData {
ValidatorComponent,
],
})
export class CreateTransactionDialogComponent extends BaseComponent implements OnInit {
private readonly dialogRef = inject(MatDialogRef<CreateTransactionDialogComponent>);
export class CreateTransactionDialogComponent extends DialogWithBaseComponent<CreateTransactionDialogData | undefined, boolean> implements OnInit {
private readonly transactionService = inject(TransactionService);
private readonly fb = inject(NonNullableFormBuilder);
private readonly categoryService = inject(CategoryService);
private readonly snackbarService = inject(SnackbarService);

data: CreateTransactionDialogData = inject(MAT_DIALOG_DATA);
data = this.dialogRef.value;

transactionTypes: TransactionType[] = Object.values(TransactionType);

Expand All @@ -62,10 +62,10 @@ export class CreateTransactionDialogComponent extends BaseComponent implements O

async ngOnInit(): Promise<void> {
this.categories = await this.categoryService.list();
if (this.data.type) {
if (this.data?.type) {
this.form.controls.type.setValue(this.data.type);
}
if (this.data.isRecurring) {
if (this.data?.isRecurring) {
this.form.controls.recurring.setValue(this.data.isRecurring);
}
this.addSubscription(this.form.controls.amount.valueChanges.subscribe(value => {
Expand All @@ -76,7 +76,7 @@ export class CreateTransactionDialogComponent extends BaseComponent implements O
}

close(): void {
this.dialogRef.close();
this.dialogRef.close(false);
}

async create(): Promise<void> {
Expand All @@ -92,9 +92,10 @@ export class CreateTransactionDialogComponent extends BaseComponent implements O
} as Transaction;
try {
const newTransaction = await this.transactionService.create(request);
this.dialogRef.close(newTransaction);
this.snackbarService.showSuccess(`Transaction '${newTransaction.title.slice(0, 10)}${newTransaction.title.length > 10 ? '...' : ''}' created successfully!`);
this.dialogRef.close(true);
} catch (_err) {
this.dialogRef.close();
this.dialogRef.close(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
<div
class="d-flex flex-column flex-md-row justify-content-center justify-content-md-between align-items-center gap-2"
>
<h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}">
<h2
class="m-0 text-center text-md-start {{
display.isSm() ? 'fs-1' : 'fs-2'
}}"
>
@if (selectedIndex === 0) {
Transactions
} @else {
Expand All @@ -20,7 +24,9 @@ <h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}"
>
<mat-tab>
<ng-template mat-tab-label>
<div class="d-flex flex-row flex-nowrap align-items-center gap-2">
<div
class="d-flex flex-row flex-nowrap align-items-center gap-2"
>
<mat-icon size="md">payments</mat-icon>
@if (display.isMd()) {
Transactions
Expand All @@ -30,7 +36,9 @@ <h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}"
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<div class="d-flex flex-row flex-nowrap align-items-center gap-2">
<div
class="d-flex flex-row flex-nowrap align-items-center gap-2"
>
<mat-icon size="md">cached</mat-icon>
@if (display.isMd()) {
Categories
Expand All @@ -55,7 +63,10 @@ <h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}"
<div class="d-flex flex-row gap-4 smaller-row">
@if (display.isLg()) {
<ex-data-table
[data]="{ transactions: recurringTransactions.incomes, categories }"
[data]="{
transactions: recurringTransactions.incomes,
categories
}"
matIcon="money_off"
class="flex-grow-1"
nonExpandable
Expand All @@ -66,7 +77,10 @@ <h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}"
(dataChangedEvent)="onDataChanged()"
/>
<ex-data-table
[data]="{ transactions: recurringTransactions.expenses, categories }"
[data]="{
transactions: recurringTransactions.expenses,
categories
}"
matIcon="money_off"
class="flex-grow-1"
nonExpandable
Expand All @@ -78,7 +92,10 @@ <h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}"
/>
} @else {
<ex-data-table
[data]="{ transactions: recurringTransactions.mergedTransactions, categories }"
[data]="{
transactions: recurringTransactions.mergedTransactions,
categories
}"
matIcon="money_off"
class="flex-grow-1"
nonExpandable
Expand All @@ -95,7 +112,11 @@ <h2 class="m-0 text-center text-md-start {{ display.isSm() ? 'fs-1' : 'fs-2' }}"
[class.overflow-hidden]="display.isMd()"
>
<div class="d-flex flex-row flex-nowrap justif-content-between">
<div [matTooltip]="!canCreateTransaction ? 'First create a category!' : ''">
<div
[matTooltip]="
!canCreateTransaction ? 'First create a category!' : ''
"
>
<ex-button
filled
matIcon="add"
Expand Down
Loading
Loading