Refactor application structure to use WorkspaceService for state management
- Removed direct state management from components and centralized it in WorkspaceService. - Updated ApartmentsPage, TenantsPage, LeasesPage, and DashboardPage to utilize WorkspaceService for data handling. - Introduced SessionService to manage user session state and authentication. - Simplified component templates to reflect new service-based architecture. - Removed unused ConfigService and its tests. - Added lease.html template for lease document generation. - Created app.routes.spec.ts for route mapping tests.
This commit is contained in:
1 parent
be1f680a62
commit
82f339e209
33 files changed
+292
-929
No files matched your search
@@ -1,10 +1,10 @@
|
||||
<section class="page-section apartments-page">
|
||||
@if (!showForm()) {
|
||||
<app-apartment-list
|
||||
[apartments]="apartments()"
|
||||
[apartments]="workspace.apartments()"
|
||||
(addRequested)="openAddForm()"
|
||||
(editRequested)="openEditForm($event)"
|
||||
(deleteRequested)="deleteRequested.emit($event)"
|
||||
(deleteRequested)="workspace.deleteApartment($event)"
|
||||
/>
|
||||
} @else {
|
||||
<app-apartment-form
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { signal } from '@angular/core';
|
||||
import { vi } from 'vitest';
|
||||
import { ApartmentsPage } from './apartments-page';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
|
||||
describe('ApartmentsPage', () => {
|
||||
const workspace = {
|
||||
apartments: signal([]),
|
||||
loadApartments: () => undefined,
|
||||
saveApartment: () => undefined,
|
||||
deleteApartment: () => undefined,
|
||||
};
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({ providers: [{ provide: WorkspaceService, useValue: workspace }] }));
|
||||
|
||||
it('opens the form for the apartment selected for editing', () => {
|
||||
const apartment = {
|
||||
id: 'apt-1',
|
||||
@@ -12,7 +23,6 @@ describe('ApartmentsPage', () => {
|
||||
description: '',
|
||||
};
|
||||
const fixture = TestBed.createComponent(ApartmentsPage);
|
||||
fixture.componentRef.setInput('apartments', [apartment]);
|
||||
fixture.detectChanges();
|
||||
|
||||
const page = fixture.componentInstance;
|
||||
@@ -22,7 +32,7 @@ describe('ApartmentsPage', () => {
|
||||
expect(page.editingApartment()).toEqual(apartment);
|
||||
});
|
||||
|
||||
it('emits the edited apartment together with its id', () => {
|
||||
it('saves the edited apartment together with its id', () => {
|
||||
const apartment = {
|
||||
id: 'apt-1',
|
||||
name: 'T2 Paris 15',
|
||||
@@ -31,19 +41,14 @@ describe('ApartmentsPage', () => {
|
||||
description: '',
|
||||
};
|
||||
const fixture = TestBed.createComponent(ApartmentsPage);
|
||||
fixture.componentRef.setInput('apartments', [apartment]);
|
||||
fixture.detectChanges();
|
||||
const page = fixture.componentInstance;
|
||||
const apartmentSaved = vi.fn();
|
||||
page.apartmentSaved.subscribe(apartmentSaved);
|
||||
const saveApartment = vi.spyOn(workspace, 'saveApartment');
|
||||
page.openEditForm(apartment);
|
||||
|
||||
page.saveApartment({ ...apartment, name: 'T2 rénové' });
|
||||
|
||||
expect(apartmentSaved).toHaveBeenCalledWith({
|
||||
apartment: { ...apartment, name: 'T2 rénové' },
|
||||
apartmentId: 'apt-1',
|
||||
});
|
||||
expect(saveApartment).toHaveBeenCalledWith({ ...apartment, name: 'T2 rénové' }, 'apt-1');
|
||||
expect(page.showForm()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,26 +1,22 @@
|
||||
import { Component, input, output, signal } from '@angular/core';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { Apartment } from '../../services/apartment.service';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
import { ApartmentForm } from '../apartment-form/apartment-form';
|
||||
import { ApartmentList } from '../apartment-list/apartment-list';
|
||||
|
||||
export interface ApartmentSaveRequest {
|
||||
apartment: Apartment;
|
||||
apartmentId?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-apartments-page',
|
||||
imports: [ApartmentForm, ApartmentList],
|
||||
templateUrl: './apartments-page.html',
|
||||
})
|
||||
export class ApartmentsPage {
|
||||
readonly apartments = input.required<Apartment[]>();
|
||||
readonly apartmentSaved = output<ApartmentSaveRequest>();
|
||||
readonly deleteRequested = output<string>();
|
||||
readonly workspace = inject(WorkspaceService);
|
||||
|
||||
readonly showForm = signal(false);
|
||||
readonly editingApartment = signal<Apartment | null>(null);
|
||||
|
||||
constructor() { this.workspace.loadApartments(); }
|
||||
|
||||
openAddForm(): void {
|
||||
this.editingApartment.set(null);
|
||||
this.showForm.set(true);
|
||||
@@ -37,7 +33,7 @@ export class ApartmentsPage {
|
||||
}
|
||||
|
||||
saveApartment(apartment: Apartment): void {
|
||||
this.apartmentSaved.emit({ apartment, apartmentId: this.editingApartment()?.id });
|
||||
this.workspace.saveApartment(apartment, this.editingApartment()?.id);
|
||||
this.closeForm();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<section class="page-section dashboard-page">
|
||||
<header class="page-heading">
|
||||
<div>
|
||||
<p class="eyebrow">Bonjour {{ username() }}</p>
|
||||
<p class="eyebrow">Bonjour {{ session.username() }}</p>
|
||||
<h1>Tableau de bord</h1>
|
||||
<p>Retrouvez rapidement les actions utiles à votre gestion locative.</p>
|
||||
</div>
|
||||
@@ -17,10 +17,10 @@
|
||||
</div>
|
||||
<div>
|
||||
<p class="eyebrow">Votre portefeuille</p>
|
||||
<h2>{{ apartmentCount() }} appartement{{ apartmentCount() > 1 ? 's' : '' }}</h2>
|
||||
<h2>{{ workspace.apartments().length }} appartement{{ workspace.apartments().length > 1 ? 's' : '' }}</h2>
|
||||
<p>Centralisez les informations essentielles de vos biens.</p>
|
||||
</div>
|
||||
<button mat-button type="button" (click)="apartmentsRequested.emit()">
|
||||
<button mat-button type="button" (click)="showApartments()">
|
||||
Gérer mes appartements
|
||||
</button>
|
||||
</article>
|
||||
@@ -28,8 +28,8 @@
|
||||
<div class="large-avatar">{{ userInitial() }}</div>
|
||||
<div class="user-details">
|
||||
<p class="eyebrow">Compte actif</p>
|
||||
<h3>{{ username() }}</h3>
|
||||
<p class="user-email">{{ email() }}</p>
|
||||
<h3>{{ session.username() }}</h3>
|
||||
<p class="user-email">{{ session.email() }}</p>
|
||||
<span class="role-chip">Bailleur connecté</span>
|
||||
</div>
|
||||
</article>
|
||||
@@ -40,7 +40,7 @@
|
||||
<p>Vérifiez que votre espace communique correctement avec le service sécurisé.</p>
|
||||
</div>
|
||||
<div class="api-actions">
|
||||
<button mat-stroked-button type="button" (click)="connectionCheckRequested.emit()">
|
||||
<button mat-stroked-button type="button" (click)="checkConnection()">
|
||||
Tester la connexion
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { Component, computed, inject, signal } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Router } from '@angular/router';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard-page',
|
||||
@@ -8,11 +12,21 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
styleUrl: './dashboard-page.css',
|
||||
})
|
||||
export class DashboardPage {
|
||||
readonly username = input.required<string>();
|
||||
readonly email = input.required<string>();
|
||||
readonly userInitial = input.required<string>();
|
||||
readonly apartmentCount = input.required<number>();
|
||||
readonly helloMessage = input.required<string>();
|
||||
readonly apartmentsRequested = output<void>();
|
||||
readonly connectionCheckRequested = output<void>();
|
||||
private readonly http = inject(HttpClient);
|
||||
private readonly router = inject(Router);
|
||||
readonly session = inject(SessionService);
|
||||
readonly workspace = inject(WorkspaceService);
|
||||
readonly userInitial = computed(() => this.session.username().charAt(0).toUpperCase());
|
||||
readonly helloMessage = signal('');
|
||||
|
||||
constructor() { this.workspace.loadApartments(); }
|
||||
|
||||
checkConnection(): void {
|
||||
this.http.get('/api/hello', { responseType: 'text' }).subscribe({
|
||||
next: (message) => this.helloMessage.set(message),
|
||||
error: (error) => this.helloMessage.set(`Failed to connect to backend: ${error.statusText}`),
|
||||
});
|
||||
}
|
||||
|
||||
showApartments(): void { this.router.navigateByUrl('/apartments'); }
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<span aria-hidden="true">+</span> Créer un bail
|
||||
</button>
|
||||
</header>
|
||||
@if (leases().length === 0) {
|
||||
@if (workspace.leases().length === 0) {
|
||||
<article class="empty-state">
|
||||
<p class="eyebrow">Mise en location</p>
|
||||
<h2>Aucun bail enregistré</h2>
|
||||
@@ -19,13 +19,13 @@
|
||||
</article>
|
||||
} @else {
|
||||
<div class="lease-grid">
|
||||
@for (lease of leases(); track lease.id) {
|
||||
@for (lease of workspace.leases(); track lease.id) {
|
||||
<app-lease-card
|
||||
[lease]="lease"
|
||||
[apartments]="apartments()"
|
||||
[tenants]="tenants()"
|
||||
(pdfRequested)="pdfRequested.emit($event)"
|
||||
(deleteRequested)="deleteRequested.emit($event)"
|
||||
[apartments]="workspace.apartments()"
|
||||
[tenants]="workspace.tenants()"
|
||||
(pdfRequested)="workspace.downloadLeasePdf($event)"
|
||||
(deleteRequested)="workspace.deleteLease($event)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
@@ -33,9 +33,9 @@
|
||||
</section>
|
||||
} @else {
|
||||
<app-lease-form
|
||||
[apartments]="apartments()"
|
||||
[tenants]="tenants()"
|
||||
[apartments]="workspace.apartments()"
|
||||
[tenants]="workspace.tenants()"
|
||||
(cancelled)="closeForm()"
|
||||
(submitted)="leaseSaved.emit($event); closeForm()"
|
||||
(submitted)="workspace.saveLease($event); closeForm()"
|
||||
/>
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { LeasesPage } from './leases-page';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
|
||||
describe('LeasesPage', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({ providers: [{ provide: WorkspaceService, useValue: { loadApartments: () => undefined, loadTenants: () => undefined, loadLeases: () => undefined } }] }));
|
||||
|
||||
it('opens and closes the focused lease form module', () => {
|
||||
const fixture = TestBed.createComponent(LeasesPage);
|
||||
fixture.componentRef.setInput('leases', []);
|
||||
fixture.componentRef.setInput('apartments', []);
|
||||
fixture.componentRef.setInput('tenants', []);
|
||||
const component = fixture.componentInstance;
|
||||
|
||||
component.openForm();
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Component, input, output, signal } from '@angular/core';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { Apartment } from '../../services/apartment.service';
|
||||
import { Lease } from '../../services/lease.service';
|
||||
import { Tenant } from '../../services/tenant.service';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
import { LeaseCard } from '../lease-card/lease-card';
|
||||
import { LeaseForm } from '../lease-form/lease-form';
|
||||
|
||||
@@ -13,14 +11,15 @@ import { LeaseForm } from '../lease-form/lease-form';
|
||||
styleUrl: './leases-page.css',
|
||||
})
|
||||
export class LeasesPage {
|
||||
readonly leases = input.required<Lease[]>();
|
||||
readonly apartments = input.required<Apartment[]>();
|
||||
readonly tenants = input.required<Tenant[]>();
|
||||
readonly leaseSaved = output<Lease>();
|
||||
readonly deleteRequested = output<string>();
|
||||
readonly pdfRequested = output<Lease>();
|
||||
readonly workspace = inject(WorkspaceService);
|
||||
readonly showForm = signal(false);
|
||||
|
||||
constructor() {
|
||||
this.workspace.loadApartments();
|
||||
this.workspace.loadTenants();
|
||||
this.workspace.loadLeases();
|
||||
}
|
||||
|
||||
openForm(): void {
|
||||
this.showForm.set(true);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<mat-radio-group
|
||||
class="theme-options"
|
||||
aria-label="Thème de l'application"
|
||||
[value]="themePreference()"
|
||||
(change)="themePreferenceChanged.emit($event.value)"
|
||||
[value]="theme.preference()"
|
||||
(change)="theme.setPreference($event.value)"
|
||||
>
|
||||
<mat-radio-button value="light"
|
||||
><span class="theme-option-copy"
|
||||
@@ -60,10 +60,10 @@
|
||||
</div>
|
||||
<div class="session-action">
|
||||
<div>
|
||||
<strong>{{ username() }}</strong
|
||||
><span>{{ email() }}</span>
|
||||
<strong>{{ session.username() }}</strong
|
||||
><span>{{ session.email() }}</span>
|
||||
</div>
|
||||
<button mat-stroked-button type="button" (click)="logoutRequested.emit()">Déconnexion</button>
|
||||
<button mat-stroked-button type="button" (click)="logout()">Déconnexion</button>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { ThemePreference } from '../../services/theme.service';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings-page',
|
||||
@@ -10,9 +11,8 @@ import { ThemePreference } from '../../services/theme.service';
|
||||
styleUrl: './settings-page.css',
|
||||
})
|
||||
export class SettingsPage {
|
||||
readonly username = input.required<string>();
|
||||
readonly email = input.required<string>();
|
||||
readonly themePreference = input.required<ThemePreference>();
|
||||
readonly themePreferenceChanged = output<ThemePreference>();
|
||||
readonly logoutRequested = output<void>();
|
||||
readonly session = inject(SessionService);
|
||||
readonly theme = inject(ThemeService);
|
||||
|
||||
logout(): void { window.location.href = '/logout'; }
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<span aria-hidden="true">+</span> Ajouter un locataire
|
||||
</button>
|
||||
</header>
|
||||
@if (tenants().length === 0) {
|
||||
@if (workspace.tenants().length === 0) {
|
||||
<article class="empty-state">
|
||||
<p class="eyebrow">Premier dossier</p>
|
||||
<h2>Aucun locataire enregistré</h2>
|
||||
@@ -21,14 +21,14 @@
|
||||
</article>
|
||||
} @else {
|
||||
<div class="tenant-grid">
|
||||
@for (tenant of tenants(); track tenant.id) {
|
||||
@for (tenant of workspace.tenants(); track tenant.id) {
|
||||
<app-tenant-card
|
||||
[tenant]="tenant"
|
||||
[attachmentError]="attachmentError()"
|
||||
[attachmentError]="workspace.tenantAttachmentError()"
|
||||
(editRequested)="openEditForm($event)"
|
||||
(deleteRequested)="deleteRequested.emit($event)"
|
||||
(attachmentUploadRequested)="attachmentUploadRequested.emit($event)"
|
||||
(attachmentDownloadRequested)="attachmentDownloadRequested.emit($event)"
|
||||
(deleteRequested)="workspace.deleteTenant($event)"
|
||||
(attachmentUploadRequested)="workspace.uploadTenantAttachment($event.tenantId, $event.file)"
|
||||
(attachmentDownloadRequested)="workspace.downloadTenantAttachment($event.tenantId, $event.attachment)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
@@ -38,6 +38,6 @@
|
||||
<app-tenant-form
|
||||
[tenant]="editingTenant()"
|
||||
(cancelled)="closeForm()"
|
||||
(submitted)="tenantSaved.emit($event); closeForm()"
|
||||
(submitted)="workspace.saveTenant($event.tenant, editingTenant()?.id); closeForm()"
|
||||
/>
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { TenantsPage } from './tenants-page';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
|
||||
describe('TenantsPage', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({ providers: [{ provide: WorkspaceService, useValue: { loadTenants: () => undefined } }] }));
|
||||
|
||||
it('opens and closes the focused tenant form module', () => {
|
||||
const fixture = TestBed.createComponent(TenantsPage);
|
||||
fixture.componentRef.setInput('tenants', []);
|
||||
const component = fixture.componentInstance;
|
||||
|
||||
component.openAddForm();
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Component, input, output, signal } from '@angular/core';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { Tenant } from '../../services/tenant.service';
|
||||
import { TenantCard, TenantAttachmentRequest } from '../tenant-card/tenant-card';
|
||||
import { TenantForm, TenantSaveRequest } from '../tenant-form/tenant-form';
|
||||
|
||||
export type { TenantAttachmentRequest, TenantSaveRequest };
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
import { TenantCard } from '../tenant-card/tenant-card';
|
||||
import { TenantForm } from '../tenant-form/tenant-form';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tenants-page',
|
||||
@@ -13,16 +12,13 @@ export type { TenantAttachmentRequest, TenantSaveRequest };
|
||||
styleUrl: './tenants-page.css',
|
||||
})
|
||||
export class TenantsPage {
|
||||
readonly tenants = input.required<Tenant[]>();
|
||||
readonly attachmentError = input('');
|
||||
readonly tenantSaved = output<TenantSaveRequest>();
|
||||
readonly deleteRequested = output<string>();
|
||||
readonly attachmentUploadRequested = output<{ tenantId: string; file: File }>();
|
||||
readonly attachmentDownloadRequested = output<TenantAttachmentRequest>();
|
||||
readonly workspace = inject(WorkspaceService);
|
||||
|
||||
readonly showForm = signal(false);
|
||||
readonly editingTenant = signal<Tenant | null>(null);
|
||||
|
||||
constructor() { this.workspace.loadTenants(); }
|
||||
|
||||
openAddForm(): void {
|
||||
this.editingTenant.set(null);
|
||||
this.showForm.set(true);
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
mat-button
|
||||
type="button"
|
||||
class="navigation-item"
|
||||
[class.active]="activeTab() === 'dashboard'"
|
||||
[attr.aria-current]="activeTab() === 'dashboard' ? 'page' : null"
|
||||
(click)="tabSelected.emit('dashboard')"
|
||||
routerLink="/dashboard"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
@@ -31,9 +30,8 @@
|
||||
mat-button
|
||||
type="button"
|
||||
class="navigation-item"
|
||||
[class.active]="activeTab() === 'apartments'"
|
||||
[attr.aria-current]="activeTab() === 'apartments' ? 'page' : null"
|
||||
(click)="tabSelected.emit('apartments')"
|
||||
routerLink="/apartments"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
@@ -51,9 +49,8 @@
|
||||
mat-button
|
||||
type="button"
|
||||
class="navigation-item"
|
||||
[class.active]="activeTab() === 'tenants'"
|
||||
[attr.aria-current]="activeTab() === 'tenants' ? 'page' : null"
|
||||
(click)="tabSelected.emit('tenants')"
|
||||
routerLink="/tenants"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
@@ -73,9 +70,8 @@
|
||||
mat-button
|
||||
type="button"
|
||||
class="navigation-item"
|
||||
[class.active]="activeTab() === 'leases'"
|
||||
[attr.aria-current]="activeTab() === 'leases' ? 'page' : null"
|
||||
(click)="tabSelected.emit('leases')"
|
||||
routerLink="/leases"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
@@ -93,9 +89,8 @@
|
||||
mat-button
|
||||
type="button"
|
||||
class="navigation-item"
|
||||
[class.active]="activeTab() === 'settings'"
|
||||
[attr.aria-current]="activeTab() === 'settings' ? 'page' : null"
|
||||
(click)="tabSelected.emit('settings')"
|
||||
routerLink="/settings"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
@@ -116,8 +111,8 @@
|
||||
<div class="sidebar-user">
|
||||
<div class="compact-avatar">{{ userInitial() }}</div>
|
||||
<div>
|
||||
<strong>{{ username() }}</strong
|
||||
><span>{{ email() }}</span>
|
||||
<strong>{{ session.username() }}</strong
|
||||
><span>{{ session.email() }}</span>
|
||||
</div>
|
||||
<button mat-button type="button" class="logout-action" (click)="logoutRequested.emit()">
|
||||
Déconnexion
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { Component, inject, input, output } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { BrandMark } from '../brand-mark';
|
||||
|
||||
export type WorkspaceTab = 'dashboard' | 'apartments' | 'tenants' | 'leases' | 'settings';
|
||||
import { SessionService } from '../../services/session.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workspace-navigation',
|
||||
imports: [BrandMark, MatButtonModule],
|
||||
imports: [BrandMark, MatButtonModule, RouterLink, RouterLinkActive],
|
||||
templateUrl: './workspace-navigation.html',
|
||||
styleUrl: './workspace-navigation.css',
|
||||
})
|
||||
export class WorkspaceNavigation {
|
||||
readonly activeTab = input.required<WorkspaceTab>();
|
||||
readonly username = input.required<string>();
|
||||
readonly email = input.required<string>();
|
||||
readonly userInitial = input.required<string>();
|
||||
readonly tabSelected = output<WorkspaceTab>();
|
||||
readonly logoutRequested = output<void>();
|
||||
readonly session = inject(SessionService);
|
||||
}
|
||||
Reference in new issue
Block a user