tychat-contracts 1.6.21 → 1.6.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +33 -33
  2. package/dist/storage/delete-object-rpc.dto.d.ts +9 -0
  3. package/dist/storage/delete-object-rpc.dto.d.ts.map +1 -0
  4. package/dist/storage/delete-object-rpc.dto.js +48 -0
  5. package/dist/storage/index.d.ts +1 -0
  6. package/dist/storage/index.d.ts.map +1 -1
  7. package/dist/storage/index.js +1 -0
  8. package/dist/storage/storage-rmq-patterns.d.ts +2 -0
  9. package/dist/storage/storage-rmq-patterns.d.ts.map +1 -1
  10. package/dist/storage/storage-rmq-patterns.js +3 -1
  11. package/jest.config.ts +5 -5
  12. package/package.json +2 -1
  13. package/src/ai/ai-usage-response.dto.ts +47 -47
  14. package/src/ai/create-ai-usage.dto.ts +43 -43
  15. package/src/analytics/analytics-emitter.helper.ts +54 -54
  16. package/src/analytics/analytics-query.dto.ts +222 -222
  17. package/src/analytics/create-analytic-event.dto.ts +85 -85
  18. package/src/analytics/event-analytic.enum.ts +119 -119
  19. package/src/appointments/appointment-procedure.dto.ts +76 -76
  20. package/src/appointments/create-appointment.dto.ts +153 -153
  21. package/src/appointments/index.ts +10 -10
  22. package/src/appointments/list-appointments-query.dto.ts +157 -157
  23. package/src/appointments/update-appointment-procedure.dto.ts +10 -10
  24. package/src/appointments/update-appointment.dto.ts +4 -4
  25. package/src/auth/refresh-token.dto.ts +10 -10
  26. package/src/billing/billing-response.dto.ts +68 -68
  27. package/src/billing/create-billing.dto.ts +70 -70
  28. package/src/billing/index.ts +6 -6
  29. package/src/billing/list-billings-query.dto.ts +61 -61
  30. package/src/billing/update-billing.dto.ts +4 -4
  31. package/src/cashback/cashback-balance-response.dto.ts +33 -33
  32. package/src/cashback/cashback-config-response.dto.ts +43 -43
  33. package/src/cashback/cashback-enums.ts +33 -33
  34. package/src/cashback/cashback-kafka-payloads.ts +102 -102
  35. package/src/cashback/cashback-kafka-topics.ts +23 -23
  36. package/src/cashback/index.ts +7 -7
  37. package/src/cashback/referral-code-response.dto.ts +49 -49
  38. package/src/cashback/use-referral-code-response.dto.ts +15 -15
  39. package/src/configurations/clinic-configuration.dto.ts +39 -39
  40. package/src/configurations/opening-hours-slot.dto.ts +41 -41
  41. package/src/configurations/update-clinic-configuration.dto.ts +39 -39
  42. package/src/connections/whatsapp-official.dto.ts +113 -113
  43. package/src/conversations/conversation-contact-filters-query.validator.spec.ts +221 -221
  44. package/src/conversations/conversation-contact-filters-query.validator.ts +10 -10
  45. package/src/conversations/conversation-intention.dto.ts +24 -24
  46. package/src/conversations/conversation-response.dto.ts +69 -69
  47. package/src/conversations/conversation-session-response.dto.ts +65 -65
  48. package/src/conversations/conversation-type.dto.ts +15 -15
  49. package/src/conversations/create-conversation-session.dto.ts +28 -28
  50. package/src/conversations/create-conversation.dto.ts +69 -69
  51. package/src/conversations/followup-config-response.dto.ts +290 -290
  52. package/src/conversations/followup-log-response.dto.ts +58 -58
  53. package/src/conversations/followup-type.dto.ts +22 -22
  54. package/src/conversations/index.ts +17 -17
  55. package/src/conversations/list-conversation-contacts-filters.dto.ts +15 -15
  56. package/src/conversations/list-conversation-contacts-query.dto.ts +81 -81
  57. package/src/conversations/satisfaction-response.dto.ts +116 -116
  58. package/src/filters/index.ts +1 -1
  59. package/src/filters/parsed-filter.dto.ts +41 -41
  60. package/src/index.ts +23 -23
  61. package/src/patients/create-patient.dto.ts +122 -122
  62. package/src/patients/index.ts +10 -10
  63. package/src/patients/list-patients-filters.dto.ts +15 -15
  64. package/src/patients/list-patients-query.dto.ts +61 -61
  65. package/src/patients/patient-document-response.dto.ts +33 -33
  66. package/src/patients/patient-history-entry.dto.ts +22 -22
  67. package/src/patients/patient-status.dto.ts +9 -9
  68. package/src/patients/update-patient-document.dto.ts +10 -10
  69. package/src/patients/update-patient.dto.ts +15 -15
  70. package/src/payment-gateway/create-gateway-payment.dto.ts +123 -123
  71. package/src/payment-gateway/gateway-payment-response.dto.ts +65 -65
  72. package/src/payment-gateway/index.ts +12 -12
  73. package/src/payment-gateway/list-gateway-payments-query.dto.ts +63 -63
  74. package/src/payment-gateway/payment-gateway-kafka-topics.ts +25 -25
  75. package/src/payment-gateway/payment-gateway.enums.ts +34 -34
  76. package/src/payment-gateway/update-gateway-payment.dto.ts +7 -7
  77. package/src/payment-gateway/webhook-payload.dto.ts +32 -32
  78. package/src/payments/create-payment.dto.ts +73 -73
  79. package/src/payments/index.ts +6 -6
  80. package/src/payments/list-payments-query.dto.ts +44 -44
  81. package/src/payments/payment-response.dto.ts +67 -67
  82. package/src/payments/update-payment.dto.ts +4 -4
  83. package/src/procedures/create-procedure.dto.ts +104 -104
  84. package/src/procedures/index.ts +6 -6
  85. package/src/procedures/list-procedures-query.dto.ts +59 -59
  86. package/src/procedures/update-procedure.dto.ts +4 -4
  87. package/src/storage/delete-object-rpc.dto.ts +28 -0
  88. package/src/storage/index.ts +1 -0
  89. package/src/storage/storage-rmq-patterns.ts +3 -0
  90. package/src/tenants/index.ts +10 -10
  91. package/src/tenants/whatsapp-provider-kind.dto.ts +12 -12
  92. package/dist/analytics/should-skip-analytics-tenant-lookup.d.ts +0 -7
  93. package/dist/analytics/should-skip-analytics-tenant-lookup.d.ts.map +0 -1
  94. package/dist/analytics/should-skip-analytics-tenant-lookup.js +0 -13
  95. package/dist/appointments/opening-hours-slot.dto.d.ts +0 -6
  96. package/dist/appointments/opening-hours-slot.dto.d.ts.map +0 -1
  97. package/dist/appointments/opening-hours-slot.dto.js +0 -2
  98. package/dist/patients/patient-procedure.dto.d.ts +0 -9
  99. package/dist/patients/patient-procedure.dto.d.ts.map +0 -1
  100. package/dist/patients/patient-procedure.dto.js +0 -79
  101. package/dist/patients/update-patient-procedure.dto.d.ts +0 -6
  102. package/dist/patients/update-patient-procedure.dto.d.ts.map +0 -1
  103. package/dist/patients/update-patient-procedure.dto.js +0 -8
  104. package/dist/payments/payment.dto.d.ts +0 -15
  105. package/dist/payments/payment.dto.d.ts.map +0 -1
  106. package/dist/payments/payment.dto.js +0 -2
  107. package/dist/tenants/create-tenant.dto.d.ts +0 -10
  108. package/dist/tenants/create-tenant.dto.d.ts.map +0 -1
  109. package/dist/tenants/create-tenant.dto.js +0 -73
  110. package/dist/tenants/tenant-slug.util.spec.d.ts +0 -2
  111. package/dist/tenants/tenant-slug.util.spec.d.ts.map +0 -1
  112. package/dist/tenants/tenant-slug.util.spec.js +0 -102
  113. package/dist/tenants/update-tenant-payload.dto.d.ts +0 -6
  114. package/dist/tenants/update-tenant-payload.dto.d.ts.map +0 -1
  115. package/dist/tenants/update-tenant-payload.dto.js +0 -25
  116. package/dist/tenants/update-tenant.dto.d.ts +0 -10
  117. package/dist/tenants/update-tenant.dto.d.ts.map +0 -1
  118. package/dist/tenants/update-tenant.dto.js +0 -78
@@ -1,222 +1,222 @@
1
- import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
- import {
3
- IsIn,
4
- IsISO8601,
5
- IsNotEmpty,
6
- IsOptional,
7
- IsPositive,
8
- IsString,
9
- MaxLength,
10
- Min,
11
- } from 'class-validator';
12
- import { Type } from 'class-transformer';
13
- import { EVENT_ANALYTIC_TYPES, EventAnalyticType } from './event-analytic.enum';
14
-
15
- /**
16
- * Grouping granularity for time-series report data.
17
- */
18
- export const ANALYTICS_GROUP_BY = ['hour', 'day', 'week', 'month'] as const;
19
- export type AnalyticsGroupBy = (typeof ANALYTICS_GROUP_BY)[number];
20
-
21
- /**
22
- * Query DTO used to request analytics reports.
23
- */
24
- export class AnalyticsReportQueryDto {
25
- @ApiProperty({
26
- description: 'Start of the reporting period (inclusive)',
27
- example: '2026-03-01T00:00:00.000Z',
28
- })
29
- @IsISO8601()
30
- startDate: string;
31
-
32
- @ApiProperty({
33
- description: 'End of the reporting period (exclusive)',
34
- example: '2026-04-01T00:00:00.000Z',
35
- })
36
- @IsISO8601()
37
- endDate: string;
38
-
39
- @ApiPropertyOptional({
40
- description: 'Filter by event type',
41
- enum: EVENT_ANALYTIC_TYPES,
42
- example: 'patient.created',
43
- })
44
- @IsOptional()
45
- @IsIn([...EVENT_ANALYTIC_TYPES])
46
- eventType?: EventAnalyticType;
47
-
48
- @ApiPropertyOptional({
49
- description: 'Group results by time granularity',
50
- enum: ANALYTICS_GROUP_BY,
51
- example: 'day',
52
- })
53
- @IsOptional()
54
- @IsIn([...ANALYTICS_GROUP_BY])
55
- groupBy?: AnalyticsGroupBy;
56
-
57
- @ApiPropertyOptional({ description: 'Page number (starts at 1)', example: 1 })
58
- @IsOptional()
59
- @Type(() => Number)
60
- @IsPositive()
61
- @Min(1)
62
- page?: number;
63
-
64
- @ApiPropertyOptional({ description: 'Records per page', example: 20 })
65
- @IsOptional()
66
- @Type(() => Number)
67
- @IsPositive()
68
- @Min(1)
69
- limit?: number;
70
- }
71
-
72
- /**
73
- * Query DTO for the dashboard summary endpoint.
74
- */
75
- export class AnalyticsDashboardQueryDto {
76
- @ApiProperty({
77
- description: 'Start of the reporting period (inclusive)',
78
- example: '2026-03-01T00:00:00.000Z',
79
- })
80
- @IsISO8601()
81
- startDate: string;
82
-
83
- @ApiProperty({
84
- description: 'End of the reporting period (exclusive)',
85
- example: '2026-04-01T00:00:00.000Z',
86
- })
87
- @IsISO8601()
88
- endDate: string;
89
- }
90
-
91
- /** Resumo financeiro agregado a partir de eventos de analytics (pagamentos + faturamento). */
92
- export class AnalyticsDashboardFinancialDto {
93
- @ApiProperty({
94
- description:
95
- 'Receita no período: soma de `payment.payed` e `billing.invoice_paid` (valores em metadata.amount)',
96
- })
97
- revenue: number;
98
-
99
- @ApiProperty({
100
- description:
101
- 'Ticket médio: revenue / número de eventos pagos (payment.payed + billing.invoice_paid); 0 se não houver pagamentos',
102
- })
103
- averageTicket: number;
104
-
105
- @ApiProperty({
106
- description:
107
- 'Valor total de procedimentos dos agendamentos ainda pendentes (status pending/processing/sended) no período: soma de `appointment_procedures.value` via JOIN com `appointments`.',
108
- })
109
- pending: number;
110
- }
111
-
112
- export class AnalyticsDashboardTopProcedureDto {
113
- @ApiProperty({ description: 'Nome do procedimento (agendamento)' })
114
- name: string;
115
-
116
- @ApiProperty({ description: 'Quantidade de ocorrências no período' })
117
- count: number;
118
- }
119
-
120
- /** Média de `message.sent` por hora do relógio durante o expediente configurado. */
121
- export class AnalyticsDashboardMessageHourDto {
122
- @ApiProperty({ description: 'Hora local (0–23) no fuso clinicTimezone' })
123
- hour: number;
124
-
125
- @ApiProperty({
126
- description:
127
- 'Média de mensagens enviadas nessa hora: totalSent / openOccurrencesInPeriod (0 se não houve expediente nessa hora no período)',
128
- })
129
- averageSent: number;
130
-
131
- @ApiProperty({ description: 'Total de eventos message.sent nessa hora local, só dentro do horário da clínica' })
132
- totalSent: number;
133
-
134
- @ApiProperty({
135
- description:
136
- 'Quantas vezes essa hora calendário integrou o expediente (dias locais no intervalo × slots de opening_hours)',
137
- })
138
- openOccurrencesInPeriod: number;
139
- }
140
-
141
- export class AnalyticsDashboardMessagePeakDto {
142
- @ApiProperty({ description: 'Hora local (0–23) com maior totalSent entre as horas de expediente' })
143
- hour: number;
144
-
145
- @ApiProperty() totalSent: number;
146
-
147
- @ApiProperty() averageSent: number;
148
- }
149
-
150
- /**
151
- * Resposta enriquecida de `GET /analytics/dashboard` (mantém campos legados `total`, `byEventType`, `byDomain`).
152
- */
153
- export class AnalyticsDashboardResponseDto {
154
- @ApiProperty({ description: 'Total de agendamentos no período (direto da tabela `appointments` filtrado por `date`)' })
155
- appointmentsCreated: number;
156
-
157
- @ApiProperty({ description: 'Agendamentos com status `finished` no período (direto da tabela `appointments`)' })
158
- appointmentsFinished: number;
159
-
160
- @ApiProperty({
161
- description:
162
- 'Total de procedimentos vinculados a agendamentos no período (direto da tabela `appointment_procedures` via JOIN com `appointments.date`)',
163
- })
164
- proceduresScheduled: number;
165
-
166
- @ApiProperty({
167
- description: 'Procedimentos com `finished = true` vinculados a agendamentos no período (direto da tabela `appointment_procedures`)',
168
- })
169
- proceduresFinished: number;
170
-
171
- @ApiProperty({
172
- description:
173
- 'Média das notas 1–5 em respostas de satisfação (followup booking + pós-atendimento); null se não houver respostas',
174
- nullable: true,
175
- })
176
- satisfactionAverage: number | null;
177
-
178
- @ApiProperty({
179
- description:
180
- 'Taxa de no-show (%): appointment.no_show / appointment.created no período (0 se não houver criações)',
181
- })
182
- noShowRatePercent: number;
183
-
184
- @ApiProperty({ type: AnalyticsDashboardFinancialDto })
185
- financialSummary: AnalyticsDashboardFinancialDto;
186
-
187
- @ApiProperty({ type: [AnalyticsDashboardTopProcedureDto], maxItems: 5 })
188
- topProcedures: AnalyticsDashboardTopProcedureDto[];
189
-
190
- @ApiProperty({
191
- description:
192
- 'Pico / distribuição de mensagens no horário da clínica: médias por hora local (só `message.sent` dentro de opening_hours). Ordenado por hora crescente. Vazio se não houver opening_hours.',
193
- type: [AnalyticsDashboardMessageHourDto],
194
- })
195
- messagesByClinicHour: AnalyticsDashboardMessageHourDto[];
196
-
197
- @ApiProperty({
198
- description: 'Hora com maior volume de mensagens no expediente; null se não houver dados ou horário configurado',
199
- nullable: true,
200
- type: AnalyticsDashboardMessagePeakDto,
201
- })
202
- messagesPeakHour: AnalyticsDashboardMessagePeakDto | null;
203
-
204
- @ApiProperty({
205
- description: 'Fuso IANA usado para hora local e opening_hours (env ANALYTICS_CLINIC_TIMEZONE ou America/Sao_Paulo)',
206
- example: 'America/Sao_Paulo',
207
- })
208
- clinicTimezone: string;
209
-
210
- @ApiProperty({ description: 'Total de eventos de analytics no período (legado)' })
211
- total: number;
212
-
213
- @ApiProperty({
214
- description: 'Contagens por eventType (legado)',
215
- })
216
- byEventType: Array<{ eventType: string; count: number }>;
217
-
218
- @ApiProperty({
219
- description: 'Contagens por domínio (prefixo antes do ponto no eventType) (legado)',
220
- })
221
- byDomain: Array<{ domain: string; count: number }>;
222
- }
1
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
+ import {
3
+ IsIn,
4
+ IsISO8601,
5
+ IsNotEmpty,
6
+ IsOptional,
7
+ IsPositive,
8
+ IsString,
9
+ MaxLength,
10
+ Min,
11
+ } from 'class-validator';
12
+ import { Type } from 'class-transformer';
13
+ import { EVENT_ANALYTIC_TYPES, EventAnalyticType } from './event-analytic.enum';
14
+
15
+ /**
16
+ * Grouping granularity for time-series report data.
17
+ */
18
+ export const ANALYTICS_GROUP_BY = ['hour', 'day', 'week', 'month'] as const;
19
+ export type AnalyticsGroupBy = (typeof ANALYTICS_GROUP_BY)[number];
20
+
21
+ /**
22
+ * Query DTO used to request analytics reports.
23
+ */
24
+ export class AnalyticsReportQueryDto {
25
+ @ApiProperty({
26
+ description: 'Start of the reporting period (inclusive)',
27
+ example: '2026-03-01T00:00:00.000Z',
28
+ })
29
+ @IsISO8601()
30
+ startDate: string;
31
+
32
+ @ApiProperty({
33
+ description: 'End of the reporting period (exclusive)',
34
+ example: '2026-04-01T00:00:00.000Z',
35
+ })
36
+ @IsISO8601()
37
+ endDate: string;
38
+
39
+ @ApiPropertyOptional({
40
+ description: 'Filter by event type',
41
+ enum: EVENT_ANALYTIC_TYPES,
42
+ example: 'patient.created',
43
+ })
44
+ @IsOptional()
45
+ @IsIn([...EVENT_ANALYTIC_TYPES])
46
+ eventType?: EventAnalyticType;
47
+
48
+ @ApiPropertyOptional({
49
+ description: 'Group results by time granularity',
50
+ enum: ANALYTICS_GROUP_BY,
51
+ example: 'day',
52
+ })
53
+ @IsOptional()
54
+ @IsIn([...ANALYTICS_GROUP_BY])
55
+ groupBy?: AnalyticsGroupBy;
56
+
57
+ @ApiPropertyOptional({ description: 'Page number (starts at 1)', example: 1 })
58
+ @IsOptional()
59
+ @Type(() => Number)
60
+ @IsPositive()
61
+ @Min(1)
62
+ page?: number;
63
+
64
+ @ApiPropertyOptional({ description: 'Records per page', example: 20 })
65
+ @IsOptional()
66
+ @Type(() => Number)
67
+ @IsPositive()
68
+ @Min(1)
69
+ limit?: number;
70
+ }
71
+
72
+ /**
73
+ * Query DTO for the dashboard summary endpoint.
74
+ */
75
+ export class AnalyticsDashboardQueryDto {
76
+ @ApiProperty({
77
+ description: 'Start of the reporting period (inclusive)',
78
+ example: '2026-03-01T00:00:00.000Z',
79
+ })
80
+ @IsISO8601()
81
+ startDate: string;
82
+
83
+ @ApiProperty({
84
+ description: 'End of the reporting period (exclusive)',
85
+ example: '2026-04-01T00:00:00.000Z',
86
+ })
87
+ @IsISO8601()
88
+ endDate: string;
89
+ }
90
+
91
+ /** Resumo financeiro agregado a partir de eventos de analytics (pagamentos + faturamento). */
92
+ export class AnalyticsDashboardFinancialDto {
93
+ @ApiProperty({
94
+ description:
95
+ 'Receita no período: soma de `payment.payed` e `billing.invoice_paid` (valores em metadata.amount)',
96
+ })
97
+ revenue: number;
98
+
99
+ @ApiProperty({
100
+ description:
101
+ 'Ticket médio: revenue / número de eventos pagos (payment.payed + billing.invoice_paid); 0 se não houver pagamentos',
102
+ })
103
+ averageTicket: number;
104
+
105
+ @ApiProperty({
106
+ description:
107
+ 'Valor total de procedimentos dos agendamentos ainda pendentes (status pending/processing/sended) no período: soma de `appointment_procedures.value` via JOIN com `appointments`.',
108
+ })
109
+ pending: number;
110
+ }
111
+
112
+ export class AnalyticsDashboardTopProcedureDto {
113
+ @ApiProperty({ description: 'Nome do procedimento (agendamento)' })
114
+ name: string;
115
+
116
+ @ApiProperty({ description: 'Quantidade de ocorrências no período' })
117
+ count: number;
118
+ }
119
+
120
+ /** Média de `message.sent` por hora do relógio durante o expediente configurado. */
121
+ export class AnalyticsDashboardMessageHourDto {
122
+ @ApiProperty({ description: 'Hora local (0–23) no fuso clinicTimezone' })
123
+ hour: number;
124
+
125
+ @ApiProperty({
126
+ description:
127
+ 'Média de mensagens enviadas nessa hora: totalSent / openOccurrencesInPeriod (0 se não houve expediente nessa hora no período)',
128
+ })
129
+ averageSent: number;
130
+
131
+ @ApiProperty({ description: 'Total de eventos message.sent nessa hora local, só dentro do horário da clínica' })
132
+ totalSent: number;
133
+
134
+ @ApiProperty({
135
+ description:
136
+ 'Quantas vezes essa hora calendário integrou o expediente (dias locais no intervalo × slots de opening_hours)',
137
+ })
138
+ openOccurrencesInPeriod: number;
139
+ }
140
+
141
+ export class AnalyticsDashboardMessagePeakDto {
142
+ @ApiProperty({ description: 'Hora local (0–23) com maior totalSent entre as horas de expediente' })
143
+ hour: number;
144
+
145
+ @ApiProperty() totalSent: number;
146
+
147
+ @ApiProperty() averageSent: number;
148
+ }
149
+
150
+ /**
151
+ * Resposta enriquecida de `GET /analytics/dashboard` (mantém campos legados `total`, `byEventType`, `byDomain`).
152
+ */
153
+ export class AnalyticsDashboardResponseDto {
154
+ @ApiProperty({ description: 'Total de agendamentos no período (direto da tabela `appointments` filtrado por `date`)' })
155
+ appointmentsCreated: number;
156
+
157
+ @ApiProperty({ description: 'Agendamentos com status `finished` no período (direto da tabela `appointments`)' })
158
+ appointmentsFinished: number;
159
+
160
+ @ApiProperty({
161
+ description:
162
+ 'Total de procedimentos vinculados a agendamentos no período (direto da tabela `appointment_procedures` via JOIN com `appointments.date`)',
163
+ })
164
+ proceduresScheduled: number;
165
+
166
+ @ApiProperty({
167
+ description: 'Procedimentos com `finished = true` vinculados a agendamentos no período (direto da tabela `appointment_procedures`)',
168
+ })
169
+ proceduresFinished: number;
170
+
171
+ @ApiProperty({
172
+ description:
173
+ 'Média das notas 1–5 em respostas de satisfação (followup booking + pós-atendimento); null se não houver respostas',
174
+ nullable: true,
175
+ })
176
+ satisfactionAverage: number | null;
177
+
178
+ @ApiProperty({
179
+ description:
180
+ 'Taxa de no-show (%): appointment.no_show / appointment.created no período (0 se não houver criações)',
181
+ })
182
+ noShowRatePercent: number;
183
+
184
+ @ApiProperty({ type: AnalyticsDashboardFinancialDto })
185
+ financialSummary: AnalyticsDashboardFinancialDto;
186
+
187
+ @ApiProperty({ type: [AnalyticsDashboardTopProcedureDto], maxItems: 5 })
188
+ topProcedures: AnalyticsDashboardTopProcedureDto[];
189
+
190
+ @ApiProperty({
191
+ description:
192
+ 'Pico / distribuição de mensagens no horário da clínica: médias por hora local (só `message.sent` dentro de opening_hours). Ordenado por hora crescente. Vazio se não houver opening_hours.',
193
+ type: [AnalyticsDashboardMessageHourDto],
194
+ })
195
+ messagesByClinicHour: AnalyticsDashboardMessageHourDto[];
196
+
197
+ @ApiProperty({
198
+ description: 'Hora com maior volume de mensagens no expediente; null se não houver dados ou horário configurado',
199
+ nullable: true,
200
+ type: AnalyticsDashboardMessagePeakDto,
201
+ })
202
+ messagesPeakHour: AnalyticsDashboardMessagePeakDto | null;
203
+
204
+ @ApiProperty({
205
+ description: 'Fuso IANA usado para hora local e opening_hours (env ANALYTICS_CLINIC_TIMEZONE ou America/Sao_Paulo)',
206
+ example: 'America/Sao_Paulo',
207
+ })
208
+ clinicTimezone: string;
209
+
210
+ @ApiProperty({ description: 'Total de eventos de analytics no período (legado)' })
211
+ total: number;
212
+
213
+ @ApiProperty({
214
+ description: 'Contagens por eventType (legado)',
215
+ })
216
+ byEventType: Array<{ eventType: string; count: number }>;
217
+
218
+ @ApiProperty({
219
+ description: 'Contagens por domínio (prefixo antes do ponto no eventType) (legado)',
220
+ })
221
+ byDomain: Array<{ domain: string; count: number }>;
222
+ }
@@ -1,85 +1,85 @@
1
- import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
- import {
3
- IsIn,
4
- IsISO8601,
5
- IsNotEmpty,
6
- IsObject,
7
- IsOptional,
8
- IsString,
9
- IsUUID,
10
- MaxLength,
11
- MinLength,
12
- } from 'class-validator';
13
- import { EVENT_ANALYTIC_TYPES, EventAnalyticType } from './event-analytic.enum';
14
-
15
- /**
16
- * Payload that every microservice must send to the analytics topic.
17
- */
18
- export class CreateAnalyticEventDto {
19
- @ApiProperty({
20
- description: 'Unique event ID for idempotent processing',
21
- example: 'f3f50ecb-fb71-4d4b-a0dd-910ef6e3fd0a',
22
- })
23
- @IsUUID()
24
- eventId: string;
25
-
26
- @ApiProperty({
27
- description: 'Tenant slug that originated the event',
28
- example: 'homolog',
29
- })
30
- @IsString()
31
- @IsNotEmpty()
32
- @MinLength(1)
33
- @MaxLength(255)
34
- tenant: string;
35
-
36
- @ApiProperty({
37
- description: 'Type of analytic event',
38
- enum: EVENT_ANALYTIC_TYPES,
39
- example: 'patient.created',
40
- })
41
- @IsIn([...EVENT_ANALYTIC_TYPES])
42
- eventType: EventAnalyticType;
43
-
44
- @ApiPropertyOptional({
45
- description: 'ID of the entity related to this event (patient, appointment, etc.)',
46
- example: '3c593727-4505-495a-a054-c052acb4cf19',
47
- })
48
- @IsOptional()
49
- @IsString()
50
- @MaxLength(255)
51
- entityId?: string;
52
-
53
- @ApiPropertyOptional({
54
- description: 'Name of the source microservice',
55
- example: 'tychat-patient-service',
56
- })
57
- @IsOptional()
58
- @IsString()
59
- @MaxLength(255)
60
- source?: string;
61
-
62
- @ApiPropertyOptional({
63
- description: 'Optional user ID who triggered the action',
64
- example: 'a1b2c3d4-e5f6-4789-abcd-ef0123456789',
65
- })
66
- @IsOptional()
67
- @IsString()
68
- @MaxLength(255)
69
- userId?: string;
70
-
71
- @ApiPropertyOptional({
72
- description: 'Additional metadata for the event (flexible JSON)',
73
- example: { previousStatus: 'pending', newStatus: 'finished' },
74
- })
75
- @IsOptional()
76
- @IsObject()
77
- metadata?: Record<string, unknown>;
78
-
79
- @ApiProperty({
80
- description: 'ISO 8601 timestamp when the event occurred',
81
- example: '2026-04-02T12:00:00.000Z',
82
- })
83
- @IsISO8601()
84
- occurredAt: string;
85
- }
1
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
+ import {
3
+ IsIn,
4
+ IsISO8601,
5
+ IsNotEmpty,
6
+ IsObject,
7
+ IsOptional,
8
+ IsString,
9
+ IsUUID,
10
+ MaxLength,
11
+ MinLength,
12
+ } from 'class-validator';
13
+ import { EVENT_ANALYTIC_TYPES, EventAnalyticType } from './event-analytic.enum';
14
+
15
+ /**
16
+ * Payload that every microservice must send to the analytics topic.
17
+ */
18
+ export class CreateAnalyticEventDto {
19
+ @ApiProperty({
20
+ description: 'Unique event ID for idempotent processing',
21
+ example: 'f3f50ecb-fb71-4d4b-a0dd-910ef6e3fd0a',
22
+ })
23
+ @IsUUID()
24
+ eventId: string;
25
+
26
+ @ApiProperty({
27
+ description: 'Tenant slug that originated the event',
28
+ example: 'homolog',
29
+ })
30
+ @IsString()
31
+ @IsNotEmpty()
32
+ @MinLength(1)
33
+ @MaxLength(255)
34
+ tenant: string;
35
+
36
+ @ApiProperty({
37
+ description: 'Type of analytic event',
38
+ enum: EVENT_ANALYTIC_TYPES,
39
+ example: 'patient.created',
40
+ })
41
+ @IsIn([...EVENT_ANALYTIC_TYPES])
42
+ eventType: EventAnalyticType;
43
+
44
+ @ApiPropertyOptional({
45
+ description: 'ID of the entity related to this event (patient, appointment, etc.)',
46
+ example: '3c593727-4505-495a-a054-c052acb4cf19',
47
+ })
48
+ @IsOptional()
49
+ @IsString()
50
+ @MaxLength(255)
51
+ entityId?: string;
52
+
53
+ @ApiPropertyOptional({
54
+ description: 'Name of the source microservice',
55
+ example: 'tychat-patient-service',
56
+ })
57
+ @IsOptional()
58
+ @IsString()
59
+ @MaxLength(255)
60
+ source?: string;
61
+
62
+ @ApiPropertyOptional({
63
+ description: 'Optional user ID who triggered the action',
64
+ example: 'a1b2c3d4-e5f6-4789-abcd-ef0123456789',
65
+ })
66
+ @IsOptional()
67
+ @IsString()
68
+ @MaxLength(255)
69
+ userId?: string;
70
+
71
+ @ApiPropertyOptional({
72
+ description: 'Additional metadata for the event (flexible JSON)',
73
+ example: { previousStatus: 'pending', newStatus: 'finished' },
74
+ })
75
+ @IsOptional()
76
+ @IsObject()
77
+ metadata?: Record<string, unknown>;
78
+
79
+ @ApiProperty({
80
+ description: 'ISO 8601 timestamp when the event occurred',
81
+ example: '2026-04-02T12:00:00.000Z',
82
+ })
83
+ @IsISO8601()
84
+ occurredAt: string;
85
+ }