# @tc-libs/angular

Libreria Angular condivisa per autenticazione, servizi HTTP base, storage, utilita e piccoli componenti standalone.

La libreria e pensata per Angular 21 e per applicazioni standalone. Tutte le API pubbliche sono esportate da `@tc-libs/angular`.

## Cosa include

- autenticazione con `AuthService`, guard, direttive e interceptor
- servizi HTTP base estendibili con `ServiceBase`, `ServiceAdminBase` e `ServicePublicBase`
- modelli e DTO condivisi (`IDatabaseMongoEntity`, `IAppResponse`, `II18n`, `ISEO`, ecc.)
- servizi utility (`PaginationService`, `SeoService`, `I18nService`, `DeviceService`, `ErrorService`, `ValidationService`, `ImageService`)
- storage per immagini e file (`StorageService`, `StorageAdminService`, `IStorageImage`, `ACL`)
- componente standalone `LongdashComponent`

## Requisiti

- Angular `^21.1.0`
- applicazione configurata con `provideHttpClient(...)`
- se usi auth, routing e SEO: `@angular/router`, `@angular/platform-browser`
- se usi `DeviceService`: `@angular/cdk`
- se usi `AuthService`: `jwt-decode`

## Installazione

Nel progetto host installa la libreria e le dipendenze runtime usate dalle API pubbliche:

```bash
npm install @tc-libs/angular jwt-decode @angular/cdk
```

In una normale applicazione Angular, `@angular/common`, `@angular/core`, `@angular/forms`, `@angular/router` e `@angular/platform-browser` sono gia presenti.

## Bootstrap minimo

Configura i token e, se vuoi usare gli interceptor DI-based, registra anche `withInterceptorsFromDi()`:

```ts
import { ApplicationConfig } from '@angular/core';
import {
  HTTP_INTERCEPTORS,
  provideHttpClient,
  withFetch,
  withInterceptorsFromDi,
} from '@angular/common/http';
import { provideRouter } from '@angular/router';
import {
  AUTH_CONFIGURATION_TOKEN,
  CONFIGURATION_TOKEN,
  CredentialsInterceptor,
  TokenInterceptor,
  TRANSLATION_CONFIGURATION_TOKEN,
} from '@tc-libs/angular';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(withFetch(), withInterceptorsFromDi()),
    {
      provide: CONFIGURATION_TOKEN,
      useValue: {
        server: {
          basePath: 'http://localhost:7002',
          apiBase: 'http://localhost:7002/api',
        },
        seo: {
          append: ' | Demo',
          title: 'Demo',
          description: 'Descrizione SEO di default',
        },
      },
    },
    {
      provide: AUTH_CONFIGURATION_TOKEN,
      useValue: {
        disabled: false,
      },
    },
    {
      provide: TRANSLATION_CONFIGURATION_TOKEN,
      useValue: {},
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true,
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: CredentialsInterceptor,
      multi: true,
    },
  ],
};
```

### Token disponibili

- `CONFIGURATION_TOKEN`: configurazione server e default SEO
- `AUTH_CONFIGURATION_TOKEN`: flag `disabled` per bypassare i controlli di autorizzazione
- `TRANSLATION_CONFIGURATION_TOKEN`: dizionario traduzioni usato da `I18nService`

### Shape di `CONFIGURATION_TOKEN`

```ts
{
  server: {
    basePath: string;
    apiBase: string;
  };
  seo: {
    append: string;
    title: string;
    description: string;
  };
}
```

`apiBase` e il campo usato dai servizi HTTP base e da `AuthService`.

## Import delle API

```ts
import {
  ACL,
  AuthService,
  CONFIGURATION_TOKEN,
  CountRequest,
  DeviceService,
  ErrorService,
  GetAllRequest,
  I18n,
  I18nService,
  IDatabaseMongoEntity,
  ISEO,
  IStorageImage,
  LongdashComponent,
  PaginationService,
  ROLE_ACCESS_FOR,
  SEO,
  SeoService,
  ServiceAdminBase,
  ServiceBase,
  ServicePublicBase,
  StorageAdminService,
  fileExtensionValidator,
  fileSizeValidator,
  isGranted,
} from '@tc-libs/angular';
```

## Autenticazione

### AuthService

`AuthService` espone i metodi principali:

- `login(credentials)`
- `register(credentials)`
- `onboarding(data)`
- `resetPassword(credentials)`
- `logout()`
- `refreshToken()`
- `getProfile()`
- `isGranted(roles)`
- `isLoggedIn()`

Gli access token e refresh token vengono salvati in `localStorage`.

### Guard e direttive

Esempio di routing:

```ts
import {
  ROLE_ACCESS_FOR,
  alreadyAuthGuard,
  authGuard,
  isGranted,
} from '@tc-libs/angular';

export const routes = [
  {
    path: 'login',
    canActivate: [alreadyAuthGuard],
    loadComponent: () => import('./login.component').then((m) => m.LoginComponent),
  },
  {
    path: 'admin',
    canActivate: [authGuard, isGranted([ROLE_ACCESS_FOR.ADMIN])],
    loadComponent: () => import('./admin.component').then((m) => m.AdminComponent),
  },
];
```

Esempio in template:

```html
<button *isGranted="[ROLE_ACCESS_FOR.ADMIN]">Solo admin</button>
```

### Nota importante sul comportamento attuale di `authGuard`

L'implementazione corrente di `authGuard` richiama `redirectToSafeUrl()` quando non trova un token, ma restituisce comunque `true`. In pratica:

- segnala che l'utente non e autenticato
- non blocca da sola la navigazione

Se nel tuo progetto vuoi bloccare davvero la route, crea una guard custom che usi `AuthService.isLoggedIn()` e ritorni `false` o una `UrlTree`.

### Modalita senza auth

Se stai costruendo una demo, uno styleguide o un backoffice senza login attivo, puoi disabilitare i controlli ruolo impostando:

```ts
{
  provide: AUTH_CONFIGURATION_TOKEN,
  useValue: { disabled: true },
}
```

Questo bypassa la direttiva `*isGranted`.

## Servizi HTTP base

La libreria fornisce tre classi astratte:

- `ServiceBase<T>`: endpoint standard
- `ServiceAdminBase<T>`: aggiunge il prefisso `admin/`
- `ServicePublicBase<T>`: aggiunge il prefisso `public/`

Per usarle basta estenderle e definire `basePath` ed `entity`.

```ts
import { Injectable } from '@angular/core';
import { ServiceAdminBase, IDatabaseMongoEntity } from '@tc-libs/angular';

export interface IProduct extends IDatabaseMongoEntity {
  title: { it: string };
}

@Injectable({ providedIn: 'root' })
export class ProductAdminService extends ServiceAdminBase<IProduct> {
  override basePath = 'product';
  override entity = 'ProductEntity';
}
```

### Metodi principali

- `getById(id, options?)`
- `getBySlug(slug)`
- `getByCode(code)`
- `getAll(options?)`
- `count(options?)`
- `create(model, options?)`
- `updateById(id, model)`
- `patchById(id, model)`
- `deleteById(id, model?)`
- `movePos(event, elements)` per entita ordinabili

Ogni servizio espone anche `messageBus$`, utile per reagire agli eventi CRUD (`onCreate`, `onUpdate`, `onDelete`, `onSuccess`).

### Query helper

Per costruire richieste paginabili o filtrabili puoi usare:

- `GetAllRequest`
- `GetByIdRequest`
- `FilterRequest`
- `CountRequest`

Esempio:

```ts
const options = new GetAllRequest({
  pagination: { page: 1, perPage: 20 },
  order: {
    orderBy: 'createdAt',
    orderDirection: 'desc',
    availableOrderBy: ['createdAt', 'title.it'],
  },
  enums: {
    active: true,
  },
});

this.productService.getAll(options).subscribe((response) => {
  console.log(response.data);
});
```

## Storage

API disponibili:

- `StorageService`
- `StorageAdminService`
- `IStorage`
- `IStorageImage`
- `StorageImage`
- `ACL`
- `IMAGE_FILE_EXTENSIONS`

Upload admin:

```ts
this.storageAdminService
  .uploadImage('ProductEntity', ACL.public_read, file)
  .subscribe();
```

`StorageAdminService.signUrl(url)` permette anche di firmare un URL lato backend.

## Utility

### PaginationService

Gestisce cambio pagina e ordinamento per componenti tabellari:

```ts
this.paginationService.onPageChange(this.reload, options, event);
this.paginationService.onSortChange(this.reload, options, defaultOrder, event);
```

### I18nService

Legge le traduzioni dal token `TRANSLATION_CONFIGURATION_TOKEN`.

Formato atteso:

```ts
{
  common: {
    default: {
      button: {
        save: {
          it: 'Salva',
        },
      },
    },
  },
}
```

Uso:

```ts
this.i18nService.translate('common.button.save');
```

La `translate()` attuale restituisce il valore italiano (`it`), quindi il dizionario va popolato almeno per quella lingua.

### SeoService

Aggiorna titolo e meta description a partire da un oggetto `ISEO`:

```ts
this.seoService.updateSeo(entity.seo);
this.seoService.noindex();
this.seoService.index();
```

### ValidationService e validator file

```ts
myControl.addValidators([
  fileSizeValidator(5),
  fileExtensionValidator(['jpg', 'png', 'webp']),
]);
```

`ValidationService` aiuta a costruire classi CSS per stati invalidi di `FormControl` e `FormGroup`.

### DeviceService

- `isMobile()`
- `isMobile$`
- `onMobile()`
- `copyFormatted(text)`

### ErrorService

Utility per formattare errori backend, in particolare risposte `422`.

### ImageService

Genera uno `srcset` a partire dalle resize `image/thumb` e `image/hd`.

## Componenti standalone

### LongdashComponent

Componente minimale esportato come `LongdashComponent` e usabile con:

```html
<tc-longdash></tc-longdash>
```

## Sviluppo locale

Comandi utili dentro questo workspace:

```bash
ng build shared
ng test shared
```

Il progetto di esempio che usa la libreria si trova in `projects/demo-bo`.
