Skip to content

Commit 9ed2a79

Browse files
feat(ui): adicionar servico de telemetria opt-in
Co-Authored-By: domingues.pedro <pehdomingues96@gmail.com>
1 parent e506611 commit 9ed2a79

13 files changed

Lines changed: 784 additions & 0 deletions

docs/guides/telemetry.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
[comment]: # (@label Telemetria)
2+
[comment]: # (@link guides/telemetry)
3+
4+
## Telemetria do PO UI
5+
6+
O PO UI oferece um serviço de telemetria **opt-in** que permite coletar dados anônimos sobre o uso dos componentes da biblioteca. Esses dados ajudam a equipe de desenvolvimento a entender quais componentes são mais utilizados, priorizar melhorias e corrigir problemas.
7+
8+
### O que é coletado
9+
10+
Os eventos de telemetria contêm apenas as seguintes informações:
11+
12+
| Campo | Descrição |
13+
|---|---|
14+
| `componentName` | Nome do componente utilizado (ex: `po-button`, `po-table`) |
15+
| `libraryVersion` | Versão da biblioteca `@po-ui/ng-components` |
16+
| `angularVersion` | Versão do Angular utilizada na aplicação |
17+
| `timestamp` | Data e hora do evento em formato ISO 8601 |
18+
| `sessionId` | Identificador aleatório da sessão do navegador |
19+
20+
> **Nenhum dado pessoal, de negócio ou sensível é coletado.** Não são rastreados dados de formulário, interações do usuário ou informações de identificação pessoal.
21+
22+
### Como habilitar
23+
24+
A telemetria está **desabilitada por padrão**. Para habilitá-la, utilize o `PoTelemetryModule.forRoot()` na configuração do seu módulo ou aplicação:
25+
26+
**Abordagem com NgModule:**
27+
28+
```typescript
29+
import { PoTelemetryModule } from '@po-ui/ng-components';
30+
31+
@NgModule({
32+
imports: [
33+
PoModule,
34+
PoTelemetryModule.forRoot({
35+
enabled: true,
36+
endpointUrl: 'https://my-telemetry-api.example.com/events',
37+
showConsentDialog: true
38+
})
39+
]
40+
})
41+
export class AppModule {}
42+
```
43+
44+
**Abordagem Standalone:**
45+
46+
```typescript
47+
import { importProvidersFrom } from '@angular/core';
48+
import { PoTelemetryModule } from '@po-ui/ng-components';
49+
50+
bootstrapApplication(AppComponent, {
51+
providers: [
52+
importProvidersFrom(PoTelemetryModule.forRoot({
53+
enabled: true,
54+
endpointUrl: 'https://my-telemetry-api.example.com/events',
55+
showConsentDialog: true
56+
}))
57+
]
58+
});
59+
```
60+
61+
### Configurações disponíveis
62+
63+
| Propriedade | Tipo | Padrão | Descrição |
64+
|---|---|---|---|
65+
| `enabled` | `boolean` | `false` | Habilita ou desabilita a coleta de telemetria |
66+
| `endpointUrl` | `string` || URL do endpoint que receberá os eventos |
67+
| `consentStorageKey` | `string` | `po-telemetry-consent` | Chave no `localStorage` para armazenar o consentimento |
68+
| `batchIntervalMs` | `number` | `30000` | Intervalo em milissegundos para envio em batch |
69+
| `showConsentDialog` | `boolean` | `true` | Exibe diálogo de consentimento na primeira vez |
70+
| `consentDialogLiterals` | `object` || Literais customizadas para o diálogo de consentimento |
71+
72+
### Como o consentimento funciona
73+
74+
O serviço de telemetria implementa um mecanismo de consentimento em duas camadas:
75+
76+
1. **Configuração do desenvolvedor**: A telemetria só é ativada quando `enabled: true` é definido na configuração.
77+
2. **Consentimento do usuário final**: Mesmo com a telemetria habilitada, os dados só são coletados após o consentimento explícito do usuário.
78+
79+
O fluxo de consentimento funciona da seguinte forma:
80+
81+
- Ao inicializar, o serviço verifica o `localStorage` pela chave configurada (`po-telemetry-consent` por padrão).
82+
- Se não houver valor armazenado e `showConsentDialog` estiver habilitado, um diálogo de confirmação é exibido ao usuário utilizando o `PoDialogService`.
83+
- Se o usuário **aceitar**, o valor `granted` é salvo no `localStorage` e a coleta de dados é iniciada.
84+
- Se o usuário **recusar**, o valor `denied` é salvo e nenhum dado é coletado.
85+
86+
### Controle programático
87+
88+
O `PoTelemetryService` expõe métodos para controle programático do consentimento:
89+
90+
```typescript
91+
import { PoTelemetryService } from '@po-ui/ng-components';
92+
93+
@Component({ ... })
94+
export class SettingsComponent {
95+
constructor(private telemetryService: PoTelemetryService) {}
96+
97+
enableTelemetry() {
98+
this.telemetryService.grantConsent();
99+
}
100+
101+
disableTelemetry() {
102+
this.telemetryService.revokeConsent();
103+
}
104+
}
105+
```
106+
107+
### Como desabilitar
108+
109+
Para desabilitar completamente a telemetria, basta:
110+
111+
- **Não importar** o `PoTelemetryModule.forRoot()` na aplicação, ou
112+
- Definir `enabled: false` na configuração
113+
114+
Para revogar o consentimento de um usuário que já havia consentido:
115+
116+
```typescript
117+
this.telemetryService.revokeConsent();
118+
```
119+
120+
### Customização do diálogo de consentimento
121+
122+
As literais do diálogo de consentimento podem ser customizadas:
123+
124+
```typescript
125+
PoTelemetryModule.forRoot({
126+
enabled: true,
127+
endpointUrl: 'https://my-telemetry-api.example.com/events',
128+
consentDialogLiterals: {
129+
title: 'Coleta de dados de uso',
130+
message: 'Gostaríamos de coletar dados anônimos sobre o uso dos componentes para melhorar a experiência. Nenhum dado pessoal é coletado. Deseja permitir?',
131+
confirm: 'Sim, permitir',
132+
cancel: 'Não, obrigado'
133+
}
134+
})
135+
```
136+
137+
### Política de privacidade recomendada
138+
139+
Se a sua aplicação utiliza a telemetria do PO UI, é recomendado incluir na sua política de privacidade uma seção informando:
140+
141+
- Que dados anônimos de uso de componentes de interface são coletados
142+
- Que nenhum dado pessoal ou de negócio é transmitido
143+
- Que o usuário pode revogar o consentimento a qualquer momento
144+
- O endpoint para o qual os dados são enviados
145+
146+
### Formato do payload
147+
148+
Os eventos são enviados em batch via `POST` para o endpoint configurado. O corpo da requisição é um array de objetos:
149+
150+
```json
151+
[
152+
{
153+
"componentName": "po-table",
154+
"libraryVersion": "21.4.0",
155+
"angularVersion": "21.0.3",
156+
"timestamp": "2026-03-05T12:00:00.000Z",
157+
"sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
158+
}
159+
]
160+
```
161+
162+
Em caso de falha no envio, os eventos são mantidos em buffer e reenviados na próxima tentativa.

projects/portal/src/app/guide/guide-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { GuideReleasesComponent } from './guides/guide-releases/guide-releases.c
1616
import { GuideSchematicsComponent } from './guides/guide-schematics/guide-schematics.component';
1717
import { GuideSyncFundamentalsComponent } from './guides/guide-sync-fundamentals/guide-sync-fundamentals.component';
1818
import { GuideSyncGetStartedComponent } from './guides/guide-sync-get-started/guide-sync-get-started.component';
19+
import { GuideTelemetryComponent } from './guides/guide-telemetry/guide-telemetry.component';
1920
import { GuideThemeServiceComponent } from './guides/guide-theme-service/guide-theme-service.component';
2021
import { GuideCreateThemeCustomizationComponent } from './guides/guide-create-theme-customization/guide-create-theme-customization.component';
2122
import { GuideGridSystemComponent } from './guides/guide-grid-system/guide-grid-system.component';
@@ -43,6 +44,7 @@ export const guidesRoutes: Routes = [
4344
{ path: 'schematics', component: GuideSchematicsComponent },
4445
{ path: 'sync-fundamentals', component: GuideSyncFundamentalsComponent },
4546
{ path: 'sync-get-started', component: GuideSyncGetStartedComponent },
47+
{ path: 'telemetry', component: GuideTelemetryComponent },
4648
{ path: 'theme-service', component: GuideThemeServiceComponent },
4749
{ path: 'create-theme-customization', component: GuideCreateThemeCustomizationComponent },
4850
{ path: 'grid-system', component: GuideGridSystemComponent },

projects/portal/src/app/guide/guide.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { GuideReleasesComponent } from './guides/guide-releases/guide-releases.c
1919
import { GuideSchematicsComponent } from './guides/guide-schematics/guide-schematics.component';
2020
import { GuideSyncFundamentalsComponent } from './guides/guide-sync-fundamentals/guide-sync-fundamentals.component';
2121
import { GuideSyncGetStartedComponent } from './guides/guide-sync-get-started/guide-sync-get-started.component';
22+
import { GuideTelemetryComponent } from './guides/guide-telemetry/guide-telemetry.component';
2223
import { GuideThemeServiceComponent } from './guides/guide-theme-service/guide-theme-service.component';
2324
import { GuideCreateThemeCustomizationComponent } from './guides/guide-create-theme-customization/guide-create-theme-customization.component';
2425
import { GuideGridSystemComponent } from './guides/guide-grid-system/guide-grid-system.component';
@@ -44,6 +45,7 @@ import { GuideTypographyComponent } from './guides/guide-typography/guide-typogr
4445
GuideSchematicsComponent,
4546
GuideSyncFundamentalsComponent,
4647
GuideSyncGetStartedComponent,
48+
GuideTelemetryComponent,
4749
GuideThemeServiceComponent,
4850
GuideCreateThemeCustomizationComponent,
4951
GuideGridSystemComponent,

projects/portal/src/app/guide/menu-guides.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export class MenuGuidesService {
2424
{ label: 'Schematics', link: 'guides/schematics' },
2525
{ label: 'Fundamentos do PO Sync', link: 'guides/sync-fundamentals' },
2626
{ label: 'Começando com o PO Sync', link: 'guides/sync-get-started' },
27+
{ label: 'Telemetria', link: 'guides/telemetry' },
2728
{ label: 'Customização de Temas usando o serviço PO-UI', link: 'guides/theme-service' },
2829
{ label: 'Criando um tema para o PO UI', link: 'guides/create-theme-customization' },
2930
{ label: 'Grid System', link: 'guides/grid-system' },

projects/ui/src/lib/services/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export * from './po-language/index';
1010
export * from './po-i18n/index';
1111
export * from './po-media-query/index';
1212
export * from './po-notification/index';
13+
export * from './po-telemetry/index';
1314
export * from './po-theme/index';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './po-telemetry-config.interface';
2+
export * from './po-telemetry.injection-token';
3+
export * from './po-telemetry.module';
4+
export * from './po-telemetry.service';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @usedBy PoTelemetryModule, PoTelemetryService
3+
*
4+
* @description
5+
*
6+
* Interface de configuração do serviço de telemetria.
7+
*/
8+
export interface PoTelemetryConfig {
9+
/** Se a telemetria está habilitada (default: false — opt-in). */
10+
enabled: boolean;
11+
12+
/** URL do endpoint que receberá os eventos. */
13+
endpointUrl: string;
14+
15+
/**
16+
* Chave no `localStorage` para armazenar consentimento do usuário.
17+
*
18+
* @default `po-telemetry-consent`
19+
*/
20+
consentStorageKey?: string;
21+
22+
/**
23+
* Intervalo em milissegundos para envio em batch.
24+
*
25+
* @default `30000`
26+
*/
27+
batchIntervalMs?: number;
28+
29+
/**
30+
* Exibir diálogo de consentimento ao usuário na primeira vez.
31+
*
32+
* @default `true`
33+
*/
34+
showConsentDialog?: boolean;
35+
36+
/** Literais customizadas para o diálogo de consentimento. */
37+
consentDialogLiterals?: {
38+
title?: string;
39+
message?: string;
40+
confirm?: string;
41+
cancel?: string;
42+
};
43+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { InjectionToken } from '@angular/core';
2+
3+
import { PoTelemetryConfig } from './po-telemetry-config.interface';
4+
5+
export const PO_TELEMETRY_CONFIG = new InjectionToken<PoTelemetryConfig>('PO_TELEMETRY_CONFIG');
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ModuleWithProviders, NgModule } from '@angular/core';
2+
3+
import { PO_TELEMETRY_CONFIG } from './po-telemetry.injection-token';
4+
import { PoTelemetryConfig } from './po-telemetry-config.interface';
5+
import { PoTelemetryService } from './po-telemetry.service';
6+
7+
/**
8+
* @description
9+
*
10+
* Módulo do serviço de telemetria do PO UI.
11+
*
12+
* Para utilização do serviço de telemetria, deve-se importar este módulo e invocar o método `forRoot`,
13+
* informando um objeto que implementa a interface `PoTelemetryConfig`.
14+
*
15+
* A telemetria é **opt-in** e requer consentimento do usuário.
16+
*
17+
* **Exemplo de configuração:**
18+
*
19+
* ```
20+
* import { PoTelemetryModule } from '@po-ui/ng-components';
21+
*
22+
* @NgModule({
23+
* imports: [
24+
* PoModule,
25+
* PoTelemetryModule.forRoot({
26+
* enabled: true,
27+
* endpointUrl: 'https://my-telemetry-api.example.com/events',
28+
* showConsentDialog: true
29+
* })
30+
* ]
31+
* })
32+
* export class AppModule {}
33+
* ```
34+
*/
35+
@NgModule({})
36+
export class PoTelemetryModule {
37+
static forRoot(config: PoTelemetryConfig): ModuleWithProviders<PoTelemetryModule> {
38+
return {
39+
ngModule: PoTelemetryModule,
40+
providers: [{ provide: PO_TELEMETRY_CONFIG, useValue: config }, PoTelemetryService]
41+
};
42+
}
43+
}

0 commit comments

Comments
 (0)