From a3ede839d51fd0d2216cae88b4d7f6e7e02dba38 Mon Sep 17 00:00:00 2001 From: Juanmabm24 Date: Tue, 11 Aug 2026 09:39:30 +0200 Subject: [PATCH 1/4] feat: add performance indexes for improved query efficiency and implement category detail panel with lazy loading --- .../migration/V7__add_performance_indexes.sql | 26 +++++ .../components/charts/charts.component.css | 9 ++ .../components/charts/charts.component.html | 80 ++++++++++++++- .../app/components/charts/charts.component.ts | 98 ++++++++++++++++++- 4 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 backend/src/main/resources/db/migration/V7__add_performance_indexes.sql diff --git a/backend/src/main/resources/db/migration/V7__add_performance_indexes.sql b/backend/src/main/resources/db/migration/V7__add_performance_indexes.sql new file mode 100644 index 0000000..d742491 --- /dev/null +++ b/backend/src/main/resources/db/migration/V7__add_performance_indexes.sql @@ -0,0 +1,26 @@ +-- Unique constraint fixes missing DB-level enforcement for user_email +ALTER TABLE users ADD UNIQUE INDEX idx_users_email (user_email); + +-- Covers pagination, recent transactions, and balance calculation (most-hit queries) +CREATE INDEX idx_transactions_account_date + ON transactions(account_id, `date` DESC); + +-- Covers charts and reports queries that filter by account + date range + type +CREATE INDEX idx_transactions_account_date_type + ON transactions(account_id, `date`, type); + +-- Covers the recurring transaction scheduler (avoids full table scan) +CREATE INDEX idx_transactions_recurring + ON transactions(is_recurring_series_parent, next_recurrence_date); + +-- Covers category dropdown queries split by user and type +CREATE INDEX idx_categories_user_type + ON categories(user_id, type); + +-- Covers default category queries split by type +CREATE INDEX idx_categories_default_type + ON categories(is_default, type); + +-- Covers pending debts widget (transaction_id FK + is_paid filter) +CREATE INDEX idx_debts_transaction_paid + ON debts(transaction_id, is_paid); diff --git a/frontend/src/app/components/charts/charts.component.css b/frontend/src/app/components/charts/charts.component.css index d2bf9c8..ea90072 100644 --- a/frontend/src/app/components/charts/charts.component.css +++ b/frontend/src/app/components/charts/charts.component.css @@ -4,6 +4,15 @@ height: auto; } +@keyframes fadeSlideIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +.category-detail-panel { + animation: fadeSlideIn 0.28s ease-out both; +} + .timeline-chart-header { gap: 1rem; } diff --git a/frontend/src/app/components/charts/charts.component.html b/frontend/src/app/components/charts/charts.component.html index 3134545..e5820fd 100644 --- a/frontend/src/app/components/charts/charts.component.html +++ b/frontend/src/app/components/charts/charts.component.html @@ -127,7 +127,7 @@

Análisis Financiero

-
+

Ingresos por Categoría

@if (pieIncomesData) { @@ -142,7 +142,8 @@

Ingresos por Categoría

+ [options]="pieChartOptions" + (chartClick)="onPieChartClick($event, TransactionType.INCOME)">
} @@ -154,7 +155,7 @@

Ingresos por Categoría

-
+

Gastos por Categoría

@if (pieExpensesData) { @@ -169,7 +170,8 @@

Gastos por Categoría

+ [options]="pieChartOptions" + (chartClick)="onPieChartClick($event, TransactionType.EXPENSE)">
} @@ -181,6 +183,76 @@

Gastos por Categoría

+ + @if (selectedCategory) { +
+ +
+
+ +

+ Transacciones de {{ selectedCategory.name }} +

+
+
+ + {{ categoryTotal | number:'1.2-2' }} € + + +
+
+ + + @if (isLoadingDetails) { +
+
+ Cargando movimientos... +
+ } + + + @if (!isLoadingDetails) { + @if (categoryTransactions.length === 0) { +
+ No hay transacciones en esta categoría para el período seleccionado. +
+ } + @if (categoryTransactions.length > 0) { +
    + @for (trans of categoryTransactions; track trans.id) { +
  • + + +
    + + {{ trans.title }} + + + {{ trans.date | date:'dd/MM/yyyy' }} + +
    + +
    + + {{ trans.type === 'EXPENSE' ? '-' : '+' }}{{ (trans.effectiveAmount ?? trans.amount) | number:'1.2-2' }} € + + ➔ +
    +
    +
  • + } +
+ } + } +
+ } +
diff --git a/frontend/src/app/components/charts/charts.component.ts b/frontend/src/app/components/charts/charts.component.ts index 6653915..88b727d 100644 --- a/frontend/src/app/components/charts/charts.component.ts +++ b/frontend/src/app/components/charts/charts.component.ts @@ -1,8 +1,9 @@ import { Component, HostListener, OnDestroy, OnInit } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Subscription, forkJoin } from 'rxjs'; -import { DecimalPipe } from '@angular/common'; +import { DecimalPipe, DatePipe } from '@angular/common'; import { FormsModule } from '@angular/forms'; +import { RouterLink } from '@angular/router'; import { BaseChartDirective } from 'ng2-charts'; import { Chart, ChartConfiguration, registerables } from 'chart.js'; @@ -11,6 +12,11 @@ import { ReportService } from '../../services/report.service'; import { BankAccountServiceService, BankAccount } from '../../services/bankAccount/bank-account-service.service'; import { PieChartDto, BarLineChartDto, TimelineChartDto, TransactionType } from '../../interfaces/chart.interface'; import { ThemeService } from '../../services/theme/theme.service'; +import { TransactionService } from '../../services/transaction/transaction.service'; +import { CategoryService } from '../../services/category/category.service'; +import { Category } from '../../interfaces/category.interface'; +import { Transaction } from '../../interfaces/transaction.interface'; +import { TransactionFilters } from '../../interfaces/pagination.interface'; // Registrar todos los componentes de Chart.js Chart.register(...registerables); @@ -18,7 +24,7 @@ Chart.register(...registerables); @Component({ selector: 'app-charts', standalone: true, - imports: [FormsModule, BaseChartDirective, DecimalPipe], + imports: [FormsModule, BaseChartDirective, DecimalPipe, DatePipe, RouterLink], templateUrl: './charts.component.html', styleUrls: ['./charts.component.css'] }) @@ -36,6 +42,13 @@ export class ChartsComponent implements OnInit, OnDestroy { selectedMonth = this.currentMonth; viewType: 'monthly' | 'yearly' = 'monthly'; // Nuevo selector de período + // Estado del panel de detalle de categoría (lazy loading) + selectedCategory: { name: string; color: string } | null = null; + categoryTransactions: Transaction[] = []; + categoryTotal = 0; + isLoadingDetails = false; + private categoryMap = new Map(); + // Estados de carga loadingPieIncomes = false; loadingPieExpenses = false; @@ -122,7 +135,9 @@ export class ChartsComponent implements OnInit, OnDestroy { private chartsService: ChartsService, private bankAccountService: BankAccountServiceService, private reportService: ReportService, - private themeService: ThemeService + private themeService: ThemeService, + private transactionService: TransactionService, + private categoryService: CategoryService ) {} ngOnInit() { @@ -131,6 +146,7 @@ export class ChartsComponent implements OnInit, OnDestroy { this.updateTimelineChartPresentation(); }); this.loadBankAccounts(); + this.loadCategories(); } ngOnDestroy() { @@ -158,10 +174,12 @@ export class ChartsComponent implements OnInit, OnDestroy { } onAccountChange() { + this.closeDetails(); this.loadCharts(); } onDateChange() { + this.closeDetails(); this.loadCharts(); } @@ -560,4 +578,76 @@ export class ChartsComponent implements OnInit, OnDestroy { ]; return months[month - 1]; } + + // ─── Category lazy-loading ─────────────────────────────────────────────── + + private loadCategories() { + forkJoin({ + income: this.categoryService.getCategoriesForType('INCOME'), + expense: this.categoryService.getCategoriesForType('EXPENSE') + }).subscribe({ + next: ({ income, expense }) => { + [...income, ...expense].forEach(cat => this.categoryMap.set(cat.name, cat)); + } + }); + } + + private getDateRange(): { dateFrom: string; dateTo: string } { + const pad = (n: number) => String(n).padStart(2, '0'); + if (this.viewType === 'monthly') { + const daysInMonth = new Date(this.selectedYear, this.selectedMonth, 0).getDate(); + return { + dateFrom: `${this.selectedYear}-${pad(this.selectedMonth)}-01`, + dateTo: `${this.selectedYear}-${pad(this.selectedMonth)}-${pad(daysInMonth)}` + }; + } + return { dateFrom: `${this.selectedYear}-01-01`, dateTo: `${this.selectedYear}-12-31` }; + } + + onPieChartClick(event: { event?: unknown; active?: object[] }, type: TransactionType) { + if (!event.active || event.active.length === 0) return; + const index = (event.active[0] as { index: number }).index; + const chartData = type === TransactionType.EXPENSE ? this.pieExpensesData : this.pieIncomesData; + if (!chartData?.labels || !chartData?.datasets) return; + const label = chartData.labels[index] as string; + const value = (chartData.datasets[0].data[index] as number) ?? 0; + const bgColors = chartData.datasets[0].backgroundColor; + const color = Array.isArray(bgColors) ? (bgColors[index] as string) : '#64748b'; + this.onCategoryClick(label, value, type, color); + } + + onCategoryClick(categoryName: string, totalAmount: number, type: TransactionType, color = '#64748b') { + const category = this.categoryMap.get(categoryName); + this.selectedCategory = { name: categoryName, color }; + this.categoryTotal = totalAmount; + this.categoryTransactions = []; + this.isLoadingDetails = true; + + const { dateFrom, dateTo } = this.getDateRange(); + const filters: TransactionFilters = { + dateFrom, + dateTo, + type: type as 'INCOME' | 'EXPENSE', + ...(category ? { categoryId: String(category.id) } : {}) + }; + + this.transactionService.getTransactionsPaginated(this.selectedAccountId!, 0, 50, filters).subscribe({ + next: (response) => { + this.categoryTransactions = category + ? response.content + : response.content.filter(t => t.category.name === categoryName); + this.isLoadingDetails = false; + }, + error: () => { + this.isLoadingDetails = false; + } + }); + } + + closeDetails() { + this.selectedCategory = null; + this.categoryTransactions = []; + this.categoryTotal = 0; + this.isLoadingDetails = false; + } } From c5ed857236d2a47929a384a88d6ea3b76ddd48d6 Mon Sep 17 00:00:00 2001 From: Juanmabm24 Date: Wed, 12 Aug 2026 10:27:40 +0200 Subject: [PATCH 2/4] feat: implement tutorial FAB and onboarding modal, refactor navbar and dashboard components --- frontend/src/app/app.component.css | 42 +++++++++++++++++++ frontend/src/app/app.component.html | 18 +++++++- frontend/src/app/app.component.ts | 25 ++++++++--- .../create-transaction-modal.component.html | 4 +- .../dashboard/dashboard.component.html | 4 -- .../dashboard/dashboard.component.ts | 28 ++----------- .../fixed-expenses-card.component.scss | 31 +++++++++++++- .../components/nav-bar/nav-bar.component.html | 18 -------- .../components/nav-bar/nav-bar.component.ts | 9 +--- 9 files changed, 116 insertions(+), 63 deletions(-) diff --git a/frontend/src/app/app.component.css b/frontend/src/app/app.component.css index 921b51d..31ca9d0 100644 --- a/frontend/src/app/app.component.css +++ b/frontend/src/app/app.component.css @@ -95,3 +95,45 @@ html, body { flex: 1; min-height: 0; /* Importante para que funcione el scroll interno */ } + +/* ---- FAB Tutorial ---- */ +.tutorial-fab { + position: fixed; + bottom: 28px; + right: 28px; + z-index: 900; + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + background: rgba(6, 182, 212, 0.12); + border: 1px solid rgba(6, 182, 212, 0.35); + border-radius: 50%; + color: #67e8f9; + cursor: pointer; + backdrop-filter: blur(8px); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(6, 182, 212, 0.1); + transition: all 0.25s ease; +} + +.tutorial-fab svg { + width: 20px; + height: 20px; +} + +.tutorial-fab:hover { + background: rgba(6, 182, 212, 0.22); + border-color: rgba(6, 182, 212, 0.6); + box-shadow: 0 6px 24px rgba(6, 182, 212, 0.25), 0 0 0 1px rgba(6, 182, 212, 0.2); + transform: translateY(-2px); +} + +@media (max-width: 540px) { + .tutorial-fab { + bottom: 20px; + right: 16px; + width: 40px; + height: 40px; + } +} diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index f786ff6..7caa59d 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -8,4 +8,20 @@
-
\ No newline at end of file + + + @if (showNavbar) { + + } + + + @if (showOnboarding) { + + } +
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 02bda6c..21e7a14 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,11 +1,12 @@ import { Component, OnInit } from '@angular/core'; -import { RouterOutlet, RouterLink, Router, NavigationEnd } from '@angular/router'; +import { RouterOutlet, RouterLink, Router, NavigationEnd, ActivatedRoute } from '@angular/router'; import { TransactionListComponent } from './components/transaction-list/transaction-list.component'; import { LoginRegisterComponent } from './components/auth-components/login-register/login-register.component'; import { NavBarComponent } from './components/nav-bar/nav-bar.component'; import { CommonModule } from '@angular/common'; import { filter } from 'rxjs/operators'; import { ThemeService } from './services/theme/theme.service'; +import { OnboardingComponent } from './components/onboarding/onboarding.component'; @Component({ selector: 'app-root', @@ -13,7 +14,8 @@ import { ThemeService } from './services/theme/theme.service'; imports: [ RouterOutlet, NavBarComponent, - CommonModule + CommonModule, + OnboardingComponent ], templateUrl: './app.component.html', styleUrl: './app.component.css' @@ -24,7 +26,8 @@ import { ThemeService } from './services/theme/theme.service'; export class AppComponent implements OnInit { title = 'smartspend-frontend'; - showNavbar = false; // Iniciar en false para evitar flash del navbar + showNavbar = false; + showOnboarding = false; constructor(private router: Router, private themeService: ThemeService) { // Aplicar tema guardado lo antes posible para evitar flash @@ -35,17 +38,29 @@ export class AppComponent implements OnInit { } ngOnInit() { - // 👈 Detectar la ruta inicial inmediatamente this.checkCurrentRoute(); - // Escucha los cambios de ruta posteriores this.router.events .pipe(filter(event => event instanceof NavigationEnd)) .subscribe((event: any) => { this.updateNavbarVisibility(event.url); + if (event.url.includes('tutorial=true')) { + this.showOnboarding = true; + this.router.navigate([], { queryParams: {}, replaceUrl: true }); + } else if (event.url.startsWith('/dashboard') && !OnboardingComponent.isCompleted()) { + this.showOnboarding = true; + } }); } + openTutorial(): void { + this.showOnboarding = true; + } + + onOnboardingClosed(): void { + this.showOnboarding = false; + } + private checkCurrentRoute() { const currentUrl = this.router.url; this.updateNavbarVisibility(currentUrl); diff --git a/frontend/src/app/components/create-transaction-modal/create-transaction-modal.component.html b/frontend/src/app/components/create-transaction-modal/create-transaction-modal.component.html index fd51257..818e66a 100644 --- a/frontend/src/app/components/create-transaction-modal/create-transaction-modal.component.html +++ b/frontend/src/app/components/create-transaction-modal/create-transaction-modal.component.html @@ -316,10 +316,10 @@

[ngModelOptions]="{ standalone: true }" placeholder="Nombre" maxlength="30" - class="flex-1 rounded-lg bg-slate-800 border border-slate-600 px-3 py-2 + class="min-w-0 flex-1 rounded-lg bg-slate-800 border border-slate-600 px-3 py-2 text-slate-200 focus:outline-none focus:ring-2 focus:ring-cyan-400 text-sm" /> -
+
€ - @if (showOnboarding) { - - } - @if (isLoading) {
diff --git a/frontend/src/app/components/dashboard/dashboard.component.ts b/frontend/src/app/components/dashboard/dashboard.component.ts index b4bacab..f49c37b 100644 --- a/frontend/src/app/components/dashboard/dashboard.component.ts +++ b/frontend/src/app/components/dashboard/dashboard.component.ts @@ -1,15 +1,13 @@ import { CommonModule } from '@angular/common'; import { Component, OnInit, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { ActivatedRoute, Router, RouterLink } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { BaseChartDirective } from 'ng2-charts'; import { Chart, ChartConfiguration, registerables } from 'chart.js'; import { BankAccount, BankAccountServiceService, CreateBankAccount} from '../../services/bankAccount/bank-account-service.service'; -// import { TransactionService } from '../../services/transaction/transaction.service'; // Este servicio no se usa directamente aquí, puede eliminarse si no se usa para otras cosas en DashboardComponent import { TransactionListComponent } from '../transaction-list/transaction-list.component'; import { CreateTransactionModalComponent } from '../create-transaction-modal/create-transaction-modal.component'; -import { OnboardingComponent } from '../onboarding/onboarding.component'; import { Transaction } from '../../interfaces/transaction.interface'; import { PendingDebtSummary } from '../../interfaces/pending-debt-summary.interface'; import { ActiveAccountService } from '../../services/active-account/active-account.service'; @@ -28,8 +26,7 @@ Chart.register(...registerables); RouterLink, TransactionListComponent, CreateTransactionModalComponent, - BaseChartDirective, - OnboardingComponent + BaseChartDirective ], templateUrl: './dashboard.component.html', styleUrl: './dashboard.component.css' @@ -52,7 +49,6 @@ export class DashboardComponent implements OnInit { successMessage = ''; showCreateTransactionModal: boolean = false; - showOnboarding = false; pendingDebtsSummary: PendingDebtSummary[] = []; isLoadingPendingDebts = false; fixedExpenses: Transaction[] = []; @@ -103,22 +99,12 @@ export class DashboardComponent implements OnInit { private analysisService: AnalysisService, private chartsService: ChartsService, private transactionService: TransactionService, - private router: Router, - private route: ActivatedRoute + private router: Router ) {} ngOnInit(): void { this.loadUserAccounts(); this.loadPendingDebtsSummary(); - if (!OnboardingComponent.isCompleted()) { - this.showOnboarding = true; - } - this.route.queryParams.subscribe(params => { - if (params['tutorial'] === 'true') { - this.showOnboarding = true; - this.router.navigate([], { queryParams: {}, replaceUrl: true }); - } - }); // Suscribirse a cambios en la cuenta activa this.activeAccountService.activeAccount$.subscribe(account => { @@ -413,12 +399,4 @@ export class DashboardComponent implements OnInit { this.router.navigate(['/transaction', transactionId]); } - openOnboarding(): void { - this.showOnboarding = true; - } - - onOnboardingClosed(): void { - this.showOnboarding = false; - } - } diff --git a/frontend/src/app/components/forecast-dashboard/fixed-expenses-card/fixed-expenses-card.component.scss b/frontend/src/app/components/forecast-dashboard/fixed-expenses-card/fixed-expenses-card.component.scss index 5664283..2e966ae 100644 --- a/frontend/src/app/components/forecast-dashboard/fixed-expenses-card/fixed-expenses-card.component.scss +++ b/frontend/src/app/components/forecast-dashboard/fixed-expenses-card/fixed-expenses-card.component.scss @@ -14,8 +14,9 @@ .panel-header { display: flex; justify-content: space-between; - align-items: center; + align-items: flex-start; margin-bottom: 1rem; + gap: 0.75rem; h2 { margin: 0.15rem 0 0; @@ -101,6 +102,12 @@ } } +.expense-main { + flex: 1; + min-width: 0; + overflow: hidden; +} + .title { margin: 0; color: var(--color-text); @@ -119,6 +126,7 @@ display: flex; flex-direction: column; align-items: flex-end; + flex-shrink: 0; } .amount { @@ -133,6 +141,7 @@ padding: 0.2rem 0.55rem; background: rgba(251, 146, 60, 0.18); color: #ffcc8a; + white-space: nowrap; &.paid { background: rgba(52, 211, 153, 0.18); @@ -154,6 +163,26 @@ opacity: 0.65; } +/* ---- Mobile: stack layout so the status badge doesn't overflow ---- */ +@media (max-width: 480px) { + .expense-item { + flex-direction: column; + gap: 0.5rem; + } + + .expense-side { + flex-direction: row; + align-items: center; + justify-content: space-between; + text-align: left; + width: 100%; + } + + .status { + font-size: 0.7rem; + } +} + :host-context(html.light-theme) { .total-badge { background: rgba(241, 245, 249, 0.96); diff --git a/frontend/src/app/components/nav-bar/nav-bar.component.html b/frontend/src/app/components/nav-bar/nav-bar.component.html index fac195c..d4c1fb0 100644 --- a/frontend/src/app/components/nav-bar/nav-bar.component.html +++ b/frontend/src/app/components/nav-bar/nav-bar.component.html @@ -16,16 +16,6 @@ [routerLink]="item.route" >{{ item.label }} - - -
} diff --git a/frontend/src/app/components/nav-bar/nav-bar.component.ts b/frontend/src/app/components/nav-bar/nav-bar.component.ts index d8185e2..047d463 100644 --- a/frontend/src/app/components/nav-bar/nav-bar.component.ts +++ b/frontend/src/app/components/nav-bar/nav-bar.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { Router, RouterLink } from '@angular/router'; +import { RouterLink } from '@angular/router'; import { CommonModule } from '@angular/common'; import { ThemeService } from '../../services/theme/theme.service'; @@ -27,7 +27,7 @@ export class NavBarComponent { isMobileMenuOpen = false; - constructor(public themeService: ThemeService, private router: Router) {} + constructor(public themeService: ThemeService) {} toggleTheme(): void { this.themeService.toggle(); @@ -40,9 +40,4 @@ export class NavBarComponent { closeMobileMenu(): void { this.isMobileMenuOpen = false; } - - openTutorial(): void { - this.closeMobileMenu(); - this.router.navigate(['/dashboard'], { queryParams: { tutorial: 'true' } }); - } } From 7d77bd6045fc9a0bbe24dc61facd2e3e538d8341 Mon Sep 17 00:00:00 2001 From: Juanmabm24 Date: Wed, 12 Aug 2026 10:34:17 +0200 Subject: [PATCH 3/4] feat: update Google sign-in button theme to 'filled_black' for login and register components --- .../auth-components/login-register/login-register.component.ts | 2 +- frontend/src/app/components/register/register.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/auth-components/login-register/login-register.component.ts b/frontend/src/app/components/auth-components/login-register/login-register.component.ts index fcc0233..d8a8dca 100644 --- a/frontend/src/app/components/auth-components/login-register/login-register.component.ts +++ b/frontend/src/app/components/auth-components/login-register/login-register.component.ts @@ -61,7 +61,7 @@ export class LoginRegisterComponent implements OnInit { window.google.accounts.id.renderButton(btnContainer, { type: 'standard', size: 'large', - theme: 'filled_blue', + theme: 'filled_black', text: 'signin_with', shape: 'rectangular', logo_alignment: 'left', diff --git a/frontend/src/app/components/register/register.component.ts b/frontend/src/app/components/register/register.component.ts index 4b85d62..55f230b 100644 --- a/frontend/src/app/components/register/register.component.ts +++ b/frontend/src/app/components/register/register.component.ts @@ -61,7 +61,7 @@ export class RegisterComponent implements OnInit { window.google.accounts.id.renderButton(btnContainer, { type: 'standard', size: 'large', - theme: 'filled_blue', + theme: 'filled_black', text: 'signup_with', shape: 'rectangular', logo_alignment: 'left', From 5362aae860b522b01f6615e53b0646d3701d10cb Mon Sep 17 00:00:00 2001 From: Juanmabm24 Date: Wed, 12 Aug 2026 11:05:57 +0200 Subject: [PATCH 4/4] feat: update Google sign-in button theme to 'filled_blue' and enhance card styles for login and register components --- .../login-register/login-register.component.css | 6 ++++++ .../login-register/login-register.component.ts | 2 +- frontend/src/app/components/register/register.component.css | 6 ++++++ frontend/src/app/components/register/register.component.ts | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/auth-components/login-register/login-register.component.css b/frontend/src/app/components/auth-components/login-register/login-register.component.css index 102e664..39390e7 100644 --- a/frontend/src/app/components/auth-components/login-register/login-register.component.css +++ b/frontend/src/app/components/auth-components/login-register/login-register.component.css @@ -179,6 +179,12 @@ margin-right: auto; margin-bottom: 0.5rem; overflow: hidden; + border-radius: 8px; +} + +/* El div que inyecta la librería GSI de Google tiene fondo blanco por defecto */ +.register-card #google-signin-btn > div { + background: transparent !important; } .register-card #google-signin-btn { diff --git a/frontend/src/app/components/auth-components/login-register/login-register.component.ts b/frontend/src/app/components/auth-components/login-register/login-register.component.ts index d8a8dca..fcc0233 100644 --- a/frontend/src/app/components/auth-components/login-register/login-register.component.ts +++ b/frontend/src/app/components/auth-components/login-register/login-register.component.ts @@ -61,7 +61,7 @@ export class LoginRegisterComponent implements OnInit { window.google.accounts.id.renderButton(btnContainer, { type: 'standard', size: 'large', - theme: 'filled_black', + theme: 'filled_blue', text: 'signin_with', shape: 'rectangular', logo_alignment: 'left', diff --git a/frontend/src/app/components/register/register.component.css b/frontend/src/app/components/register/register.component.css index dff8b06..41c90c9 100644 --- a/frontend/src/app/components/register/register.component.css +++ b/frontend/src/app/components/register/register.component.css @@ -149,6 +149,12 @@ margin-right: auto; margin-bottom: 0.5rem; overflow: hidden; + border-radius: 8px; +} + +/* El div que inyecta la librería GSI de Google tiene fondo blanco por defecto */ +.register-card #google-register-btn > div { + background: transparent !important; } .register-card #google-register-btn { diff --git a/frontend/src/app/components/register/register.component.ts b/frontend/src/app/components/register/register.component.ts index 55f230b..4b85d62 100644 --- a/frontend/src/app/components/register/register.component.ts +++ b/frontend/src/app/components/register/register.component.ts @@ -61,7 +61,7 @@ export class RegisterComponent implements OnInit { window.google.accounts.id.renderButton(btnContainer, { type: 'standard', size: 'large', - theme: 'filled_black', + theme: 'filled_blue', text: 'signup_with', shape: 'rectangular', logo_alignment: 'left',