@hed-hog/core 0.0.295 → 0.0.296
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.
- package/dist/auth/auth.controller.d.ts +4 -4
- package/dist/auth/auth.service.d.ts +4 -4
- package/dist/challenge/challenge.service.d.ts +2 -2
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +4 -1
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +1 -1
- package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +2 -2
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/integration/index.d.ts +4 -0
- package/dist/integration/index.d.ts.map +1 -0
- package/dist/integration/index.js +20 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/integration/integration-api.validation.d.ts +2 -0
- package/dist/integration/integration-api.validation.d.ts.map +1 -0
- package/dist/integration/integration-api.validation.js +126 -0
- package/dist/integration/integration-api.validation.js.map +1 -0
- package/dist/integration/integration.module.d.ts +3 -0
- package/dist/integration/integration.module.d.ts.map +1 -0
- package/dist/integration/integration.module.js +54 -0
- package/dist/integration/integration.module.js.map +1 -0
- package/dist/integration/services/domain-event.publisher.d.ts +31 -0
- package/dist/integration/services/domain-event.publisher.d.ts.map +1 -0
- package/dist/integration/services/domain-event.publisher.js +79 -0
- package/dist/integration/services/domain-event.publisher.js.map +1 -0
- package/dist/integration/services/event-subscriber.registry.d.ts +37 -0
- package/dist/integration/services/event-subscriber.registry.d.ts.map +1 -0
- package/dist/integration/services/event-subscriber.registry.js +86 -0
- package/dist/integration/services/event-subscriber.registry.js.map +1 -0
- package/dist/integration/services/inbox.service.d.ts +55 -0
- package/dist/integration/services/inbox.service.d.ts.map +1 -0
- package/dist/integration/services/inbox.service.js +173 -0
- package/dist/integration/services/inbox.service.js.map +1 -0
- package/dist/integration/services/index.d.ts +12 -0
- package/dist/integration/services/index.d.ts.map +1 -0
- package/dist/integration/services/index.js +28 -0
- package/dist/integration/services/index.js.map +1 -0
- package/dist/integration/services/integration-developer-api.service.d.ts +30 -0
- package/dist/integration/services/integration-developer-api.service.d.ts.map +1 -0
- package/dist/integration/services/integration-developer-api.service.js +55 -0
- package/dist/integration/services/integration-developer-api.service.js.map +1 -0
- package/dist/integration/services/integration-link.service.d.ts +52 -0
- package/dist/integration/services/integration-link.service.d.ts.map +1 -0
- package/dist/integration/services/integration-link.service.js +128 -0
- package/dist/integration/services/integration-link.service.js.map +1 -0
- package/dist/integration/services/integration-settings.service.d.ts +23 -0
- package/dist/integration/services/integration-settings.service.d.ts.map +1 -0
- package/dist/integration/services/integration-settings.service.js +81 -0
- package/dist/integration/services/integration-settings.service.js.map +1 -0
- package/dist/integration/services/outbox-polling.coordinator.d.ts +45 -0
- package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -0
- package/dist/integration/services/outbox-polling.coordinator.js +143 -0
- package/dist/integration/services/outbox-polling.coordinator.js.map +1 -0
- package/dist/integration/services/outbox.notifier.d.ts +30 -0
- package/dist/integration/services/outbox.notifier.d.ts.map +1 -0
- package/dist/integration/services/outbox.notifier.js +57 -0
- package/dist/integration/services/outbox.notifier.js.map +1 -0
- package/dist/integration/services/outbox.processor.d.ts +42 -0
- package/dist/integration/services/outbox.processor.d.ts.map +1 -0
- package/dist/integration/services/outbox.processor.job.d.ts +43 -0
- package/dist/integration/services/outbox.processor.job.d.ts.map +1 -0
- package/dist/integration/services/outbox.processor.job.js +100 -0
- package/dist/integration/services/outbox.processor.job.js.map +1 -0
- package/dist/integration/services/outbox.processor.js +208 -0
- package/dist/integration/services/outbox.processor.js.map +1 -0
- package/dist/integration/services/outbox.service.d.ts +53 -0
- package/dist/integration/services/outbox.service.d.ts.map +1 -0
- package/dist/integration/services/outbox.service.js +149 -0
- package/dist/integration/services/outbox.service.js.map +1 -0
- package/dist/integration/types/event.types.d.ts +88 -0
- package/dist/integration/types/event.types.d.ts.map +1 -0
- package/dist/integration/types/event.types.js +35 -0
- package/dist/integration/types/event.types.js.map +1 -0
- package/dist/integration/types/index.d.ts +3 -0
- package/dist/integration/types/index.d.ts.map +1 -0
- package/dist/integration/types/index.js +19 -0
- package/dist/integration/types/index.js.map +1 -0
- package/dist/integration/types/subscriber.types.d.ts +31 -0
- package/dist/integration/types/subscriber.types.d.ts.map +1 -0
- package/dist/integration/types/subscriber.types.js +3 -0
- package/dist/integration/types/subscriber.types.js.map +1 -0
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +3 -3
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +3 -3
- package/dist/profile/profile.service.d.ts +3 -3
- package/dist/setting/setting.controller.d.ts +12 -8
- package/dist/setting/setting.controller.d.ts.map +1 -1
- package/dist/setting/setting.service.d.ts +12 -8
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +21 -1
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +4 -4
- package/dist/user/user.service.d.ts +9 -9
- package/hedhog/data/dashboard_component_role.yaml +223 -223
- package/hedhog/data/dashboard_role.yaml +18 -18
- package/hedhog/data/route.yaml +2 -0
- package/hedhog/data/setting_group.yaml +955 -470
- package/hedhog/data/setting_subgroup.yaml +303 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +44 -18
- package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +134 -27
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +84 -23
- package/hedhog/frontend/app/dashboard/components/widgets/permissions-chart.tsx.ejs +62 -62
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +29 -29
- package/hedhog/frontend/app/preferences/page.tsx.ejs +2 -5
- package/hedhog/table/inbox_event.yaml +40 -0
- package/hedhog/table/integration_link.yaml +33 -0
- package/hedhog/table/outbox_event.yaml +45 -0
- package/hedhog/table/setting.yaml +7 -0
- package/hedhog/table/setting_subgroup.yaml +19 -0
- package/package.json +8 -8
- package/src/ai/ai.service.ts +3 -3
- package/src/auth/auth.controller.ts +11 -11
- package/src/auth/auth.service.ts +8 -8
- package/src/core.module.ts +4 -1
- package/src/index.ts +15 -0
- package/src/integration/README.md +397 -0
- package/src/integration/USAGE_EXAMPLE.md +279 -0
- package/src/integration/index.ts +4 -0
- package/src/integration/integration-api.validation.ts +154 -0
- package/src/integration/integration.module.ts +53 -0
- package/src/integration/services/domain-event.publisher.ts +136 -0
- package/src/integration/services/event-subscriber.registry.ts +89 -0
- package/src/integration/services/inbox.service.ts +218 -0
- package/src/integration/services/index.ts +12 -0
- package/src/integration/services/integration-developer-api.service.ts +96 -0
- package/src/integration/services/integration-link.service.ts +154 -0
- package/src/integration/services/integration-settings.service.ts +128 -0
- package/src/integration/services/outbox-polling.coordinator.ts +146 -0
- package/src/integration/services/outbox.notifier.ts +48 -0
- package/src/integration/services/outbox.processor.job.ts +97 -0
- package/src/integration/services/outbox.processor.ts +266 -0
- package/src/integration/services/outbox.service.ts +209 -0
- package/src/integration/types/event.types.ts +93 -0
- package/src/integration/types/index.ts +3 -0
- package/src/integration/types/subscriber.types.ts +37 -0
- package/src/oauth/oauth.controller.ts +17 -17
- package/src/oauth/oauth.service.ts +20 -20
- package/src/setting/setting.service.ts +27 -2
- package/src/task/task.service.ts +5 -5
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
- slug: database-cleanup
|
|
2
|
+
name:
|
|
3
|
+
en: Database Cleanup
|
|
4
|
+
pt: Limpeza do Banco de Dados
|
|
5
|
+
description:
|
|
6
|
+
en: Settings for cleanup routines and database maintenance jobs
|
|
7
|
+
pt: Configurações para rotinas de limpeza e tarefas de manutenção do banco de dados
|
|
8
|
+
|
|
9
|
+
- slug: session-retention
|
|
10
|
+
name:
|
|
11
|
+
en: Session Retention
|
|
12
|
+
pt: Retenção de Sessões
|
|
13
|
+
description:
|
|
14
|
+
en: Rules for retaining and removing expired user sessions
|
|
15
|
+
pt: Regras para retenção e remoção de sessões expiradas de usuários
|
|
16
|
+
|
|
17
|
+
- slug: email-password-auth
|
|
18
|
+
name:
|
|
19
|
+
en: Email and Password Authentication
|
|
20
|
+
pt: Autenticação por Email e Senha
|
|
21
|
+
description:
|
|
22
|
+
en: Controls for traditional email and password sign-in
|
|
23
|
+
pt: Controles para autenticação tradicional com email e senha
|
|
24
|
+
|
|
25
|
+
- slug: password-policy
|
|
26
|
+
name:
|
|
27
|
+
en: Password Policy
|
|
28
|
+
pt: Política de Senha
|
|
29
|
+
description:
|
|
30
|
+
en: Requirements and validation rules for user passwords
|
|
31
|
+
pt: Requisitos e regras de validação para senhas de usuários
|
|
32
|
+
|
|
33
|
+
- slug: email-restrictions
|
|
34
|
+
name:
|
|
35
|
+
en: Email Restrictions
|
|
36
|
+
pt: Restrições de Email
|
|
37
|
+
description:
|
|
38
|
+
en: Domain allowlists and blocklists for email-based access
|
|
39
|
+
pt: Listas de permissão e bloqueio de domínios para acesso baseado em email
|
|
40
|
+
|
|
41
|
+
- slug: mfa-general
|
|
42
|
+
name:
|
|
43
|
+
en: MFA General
|
|
44
|
+
pt: MFA Geral
|
|
45
|
+
description:
|
|
46
|
+
en: Core multi-factor authentication behavior and challenge settings
|
|
47
|
+
pt: Comportamento principal da autenticação multifator e configurações de desafio
|
|
48
|
+
|
|
49
|
+
- slug: mfa-totp
|
|
50
|
+
name:
|
|
51
|
+
en: MFA TOTP
|
|
52
|
+
pt: MFA TOTP
|
|
53
|
+
description:
|
|
54
|
+
en: Time-based one-time password configuration
|
|
55
|
+
pt: Configuração de senhas temporárias baseadas em tempo
|
|
56
|
+
|
|
57
|
+
- slug: mfa-email
|
|
58
|
+
name:
|
|
59
|
+
en: MFA Email
|
|
60
|
+
pt: MFA por Email
|
|
61
|
+
description:
|
|
62
|
+
en: Email-based multi-factor authentication settings
|
|
63
|
+
pt: Configurações de autenticação multifator baseada em email
|
|
64
|
+
|
|
65
|
+
- slug: token-configuration
|
|
66
|
+
name:
|
|
67
|
+
en: Token Configuration
|
|
68
|
+
pt: Configuração de Tokens
|
|
69
|
+
description:
|
|
70
|
+
en: Access and refresh token lifetime settings
|
|
71
|
+
pt: Configurações de duração de tokens de acesso e atualização
|
|
72
|
+
|
|
73
|
+
- slug: session-management
|
|
74
|
+
name:
|
|
75
|
+
en: Session Management
|
|
76
|
+
pt: Gerenciamento de Sessões
|
|
77
|
+
description:
|
|
78
|
+
en: Limits and policies for concurrent user sessions
|
|
79
|
+
pt: Limites e políticas para sessões simultâneas de usuários
|
|
80
|
+
|
|
81
|
+
- slug: regional-settings
|
|
82
|
+
name:
|
|
83
|
+
en: Regional Settings
|
|
84
|
+
pt: Configurações Regionais
|
|
85
|
+
description:
|
|
86
|
+
en: Default language and timezone options for the system
|
|
87
|
+
pt: Opções padrão de idioma e fuso horário para o sistema
|
|
88
|
+
|
|
89
|
+
- slug: date-time-formats
|
|
90
|
+
name:
|
|
91
|
+
en: Date and Time Formats
|
|
92
|
+
pt: Formatos de Data e Hora
|
|
93
|
+
description:
|
|
94
|
+
en: Formatting rules for dates and times across the application
|
|
95
|
+
pt: Regras de formatação para datas e horários em toda a aplicação
|
|
96
|
+
|
|
97
|
+
- slug: oauth-general
|
|
98
|
+
name:
|
|
99
|
+
en: OAuth General
|
|
100
|
+
pt: OAuth Geral
|
|
101
|
+
description:
|
|
102
|
+
en: Shared settings for OAuth providers and role assignment
|
|
103
|
+
pt: Configurações compartilhadas para provedores OAuth e atribuição de funções
|
|
104
|
+
|
|
105
|
+
- slug: google
|
|
106
|
+
name:
|
|
107
|
+
en: Google
|
|
108
|
+
pt: Google
|
|
109
|
+
description:
|
|
110
|
+
en: Google OAuth Provider
|
|
111
|
+
pt: Provedor OAuth do Google
|
|
112
|
+
|
|
113
|
+
- slug: facebook
|
|
114
|
+
name:
|
|
115
|
+
en: Facebook
|
|
116
|
+
pt: Facebook
|
|
117
|
+
description:
|
|
118
|
+
en: Facebook OAuth Provider
|
|
119
|
+
pt: Provedor OAuth do Facebook
|
|
120
|
+
|
|
121
|
+
- slug: microsoft
|
|
122
|
+
name:
|
|
123
|
+
en: Microsoft
|
|
124
|
+
pt: Microsoft
|
|
125
|
+
description:
|
|
126
|
+
en: Microsoft OAuth Provider
|
|
127
|
+
pt: Provedor OAuth da Microsoft
|
|
128
|
+
|
|
129
|
+
- slug: github
|
|
130
|
+
name:
|
|
131
|
+
en: GitHub
|
|
132
|
+
pt: GitHub
|
|
133
|
+
description:
|
|
134
|
+
en: GitHub OAuth Provider
|
|
135
|
+
pt: Provedor OAuth do GitHub
|
|
136
|
+
|
|
137
|
+
- slug: microsoft-entra-id
|
|
138
|
+
name:
|
|
139
|
+
en: Microsoft Entra ID
|
|
140
|
+
pt: Microsoft Entra ID
|
|
141
|
+
description:
|
|
142
|
+
en: Microsoft Entra ID (Azure AD) OAuth Provider
|
|
143
|
+
pt: Provedor OAuth Microsoft Entra ID (Azure AD)
|
|
144
|
+
|
|
145
|
+
- slug: theme-selector
|
|
146
|
+
name:
|
|
147
|
+
en: Theme Selector
|
|
148
|
+
pt: Seletor de Tema
|
|
149
|
+
description:
|
|
150
|
+
en: Global theme mode selection settings
|
|
151
|
+
pt: Configurações de seleção global do modo de tema
|
|
152
|
+
|
|
153
|
+
- slug: light-theme-colors
|
|
154
|
+
name:
|
|
155
|
+
en: Light Theme Colors
|
|
156
|
+
pt: Cores do Tema Claro
|
|
157
|
+
description:
|
|
158
|
+
en: Palette configuration for the light theme
|
|
159
|
+
pt: Configuração da paleta para o tema claro
|
|
160
|
+
|
|
161
|
+
- slug: dark-theme-colors
|
|
162
|
+
name:
|
|
163
|
+
en: Dark Theme Colors
|
|
164
|
+
pt: Cores do Tema Escuro
|
|
165
|
+
description:
|
|
166
|
+
en: Palette configuration for the dark theme
|
|
167
|
+
pt: Configuração da paleta para o tema escuro
|
|
168
|
+
|
|
169
|
+
- slug: theme-customization
|
|
170
|
+
name:
|
|
171
|
+
en: Theme Customization
|
|
172
|
+
pt: Personalização de Tema
|
|
173
|
+
description:
|
|
174
|
+
en: Layout, typography, and other theme customization options
|
|
175
|
+
pt: Opções de layout, tipografia e outras personalizações do tema
|
|
176
|
+
|
|
177
|
+
- slug: branding
|
|
178
|
+
name:
|
|
179
|
+
en: Branding
|
|
180
|
+
pt: Identidade Visual
|
|
181
|
+
description:
|
|
182
|
+
en: Logo, icon, and brand presentation settings
|
|
183
|
+
pt: Configurações de logo, ícone e apresentação da marca
|
|
184
|
+
|
|
185
|
+
- slug: storage-general
|
|
186
|
+
name:
|
|
187
|
+
en: Storage General
|
|
188
|
+
pt: Armazenamento Geral
|
|
189
|
+
description:
|
|
190
|
+
en: Shared configuration for file storage providers and limits
|
|
191
|
+
pt: Configuração compartilhada para provedores de armazenamento e limites
|
|
192
|
+
|
|
193
|
+
- slug: local-storage
|
|
194
|
+
name:
|
|
195
|
+
en: Local Storage
|
|
196
|
+
pt: Armazenamento Local
|
|
197
|
+
description:
|
|
198
|
+
en: Filesystem-based storage configuration
|
|
199
|
+
pt: Configuração de armazenamento baseado em sistema de arquivos
|
|
200
|
+
|
|
201
|
+
- slug: s3-storage
|
|
202
|
+
name:
|
|
203
|
+
en: Amazon S3 Storage
|
|
204
|
+
pt: Armazenamento Amazon S3
|
|
205
|
+
description:
|
|
206
|
+
en: AWS S3 bucket access and storage settings
|
|
207
|
+
pt: Configurações de acesso e armazenamento em bucket AWS S3
|
|
208
|
+
|
|
209
|
+
- slug: abs-storage
|
|
210
|
+
name:
|
|
211
|
+
en: Azure Blob Storage
|
|
212
|
+
pt: Armazenamento Azure Blob
|
|
213
|
+
description:
|
|
214
|
+
en: Azure Blob Storage account and container settings
|
|
215
|
+
pt: Configurações de conta e contêiner do Azure Blob Storage
|
|
216
|
+
|
|
217
|
+
- slug: gcs-storage
|
|
218
|
+
name:
|
|
219
|
+
en: Google Cloud Storage
|
|
220
|
+
pt: Armazenamento Google Cloud
|
|
221
|
+
description:
|
|
222
|
+
en: Google Cloud Storage authentication settings
|
|
223
|
+
pt: Configurações de autenticação do Google Cloud Storage
|
|
224
|
+
|
|
225
|
+
- slug: mail-general
|
|
226
|
+
name:
|
|
227
|
+
en: Mail General
|
|
228
|
+
pt: Email Geral
|
|
229
|
+
description:
|
|
230
|
+
en: Shared mail delivery settings and provider selection
|
|
231
|
+
pt: Configurações compartilhadas de entrega de email e seleção de provedor
|
|
232
|
+
|
|
233
|
+
- slug: smtp-config
|
|
234
|
+
name:
|
|
235
|
+
en: SMTP Configuration
|
|
236
|
+
pt: Configuração SMTP
|
|
237
|
+
description:
|
|
238
|
+
en: SMTP server connection and credential settings
|
|
239
|
+
pt: Configurações de conexão e credenciais do servidor SMTP
|
|
240
|
+
|
|
241
|
+
- slug: gmail-config
|
|
242
|
+
name:
|
|
243
|
+
en: Gmail Configuration
|
|
244
|
+
pt: Configuração do Gmail
|
|
245
|
+
description:
|
|
246
|
+
en: OAuth settings for Gmail-based email delivery
|
|
247
|
+
pt: Configurações OAuth para envio de email via Gmail
|
|
248
|
+
|
|
249
|
+
- slug: ses-config
|
|
250
|
+
name:
|
|
251
|
+
en: Amazon SES Configuration
|
|
252
|
+
pt: Configuração do Amazon SES
|
|
253
|
+
description:
|
|
254
|
+
en: AWS SES credentials and regional settings
|
|
255
|
+
pt: Credenciais e configurações regionais do AWS SES
|
|
256
|
+
|
|
257
|
+
- slug: openai
|
|
258
|
+
name:
|
|
259
|
+
en: OpenAI
|
|
260
|
+
pt: OpenAI
|
|
261
|
+
description:
|
|
262
|
+
en: OpenAI service credential settings
|
|
263
|
+
pt: Configurações de credenciais do serviço OpenAI
|
|
264
|
+
|
|
265
|
+
- slug: gemini
|
|
266
|
+
name:
|
|
267
|
+
en: Gemini
|
|
268
|
+
pt: Gemini
|
|
269
|
+
description:
|
|
270
|
+
en: Google Gemini service credential settings
|
|
271
|
+
pt: Configurações de credenciais do serviço Google Gemini
|
|
272
|
+
|
|
273
|
+
- slug: outbox-general
|
|
274
|
+
name:
|
|
275
|
+
en: Outbox General
|
|
276
|
+
pt: Outbox Geral
|
|
277
|
+
description:
|
|
278
|
+
en: Core settings for the outbox event delivery system
|
|
279
|
+
pt: Configurações principais do sistema de entrega de eventos outbox
|
|
280
|
+
|
|
281
|
+
- slug: startup-behavior
|
|
282
|
+
name:
|
|
283
|
+
en: Startup Behavior
|
|
284
|
+
pt: Comportamento na Inicialização
|
|
285
|
+
description:
|
|
286
|
+
en: Startup processing settings for pending outbox events
|
|
287
|
+
pt: Configurações de processamento na inicialização para eventos pendentes do outbox
|
|
288
|
+
|
|
289
|
+
- slug: processing-config
|
|
290
|
+
name:
|
|
291
|
+
en: Processing Configuration
|
|
292
|
+
pt: Configuração de Processamento
|
|
293
|
+
description:
|
|
294
|
+
en: Background processing cadence and batch settings for outbox events
|
|
295
|
+
pt: Configurações de frequência e lotes do processamento em segundo plano dos eventos outbox
|
|
296
|
+
|
|
297
|
+
- slug: retry-config
|
|
298
|
+
name:
|
|
299
|
+
en: Retry Configuration
|
|
300
|
+
pt: Configuração de Reprocessamento
|
|
301
|
+
description:
|
|
302
|
+
en: Retry, backoff, and dead letter handling for failed outbox events
|
|
303
|
+
pt: Configurações de reprocessamento, backoff e fila de falha para eventos outbox com erro
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
SettingTypeEnum,
|
|
20
20
|
} from '@hed-hog/api-types';
|
|
21
21
|
import { useApp } from '@hed-hog/next-app-provider';
|
|
22
|
-
import { Upload } from 'lucide-react';
|
|
22
|
+
import { Eye, EyeOff, Upload } from 'lucide-react';
|
|
23
23
|
import { useTranslations } from 'next-intl';
|
|
24
24
|
import { useEffect, useRef, useState } from 'react';
|
|
25
25
|
|
|
@@ -34,6 +34,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
34
34
|
const component =
|
|
35
35
|
setting.component?.replaceAll('_', '-') || SettingComponentEnum.INPUT_TEXT;
|
|
36
36
|
const [localValue, setLocalValue] = useState(setting.value);
|
|
37
|
+
const [showSecretValue, setShowSecretValue] = useState(false);
|
|
37
38
|
const debounceTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
|
38
39
|
const isTextInputRef = useRef<boolean>(false);
|
|
39
40
|
|
|
@@ -154,7 +155,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
154
155
|
type="text"
|
|
155
156
|
value={parsedValue || ''}
|
|
156
157
|
onChange={(e) => handleChangeValue(formatValue(e.target.value))}
|
|
157
|
-
className={cn(className, 'bg-background min-w-
|
|
158
|
+
className={cn(className, 'w-full min-w-0 bg-background md:min-w-50')}
|
|
158
159
|
/>
|
|
159
160
|
);
|
|
160
161
|
|
|
@@ -167,19 +168,38 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
167
168
|
onChange={(e) =>
|
|
168
169
|
handleChangeValue(formatValue(parseFloat(e.target.value) || 0))
|
|
169
170
|
}
|
|
170
|
-
className={cn(className, 'bg-background min-w-
|
|
171
|
+
className={cn(className, 'w-full min-w-0 bg-background md:min-w-50')}
|
|
171
172
|
/>
|
|
172
173
|
);
|
|
173
174
|
|
|
174
175
|
case SettingComponentEnum.INPUT_SECRET:
|
|
175
176
|
isTextInputRef.current = true;
|
|
176
177
|
return (
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
<div className="relative w-full min-w-0 md:min-w-50">
|
|
179
|
+
<Input
|
|
180
|
+
type={showSecretValue ? 'text' : 'password'}
|
|
181
|
+
value={parsedValue || ''}
|
|
182
|
+
onChange={(e) => handleChangeValue(formatValue(e.target.value))}
|
|
183
|
+
className={cn(
|
|
184
|
+
className,
|
|
185
|
+
'w-full min-w-0 bg-background pr-10 md:min-w-50'
|
|
186
|
+
)}
|
|
187
|
+
/>
|
|
188
|
+
<Button
|
|
189
|
+
type="button"
|
|
190
|
+
variant="ghost"
|
|
191
|
+
size="icon"
|
|
192
|
+
className="absolute right-1 top-1/2 h-8 w-8 -translate-y-1/2"
|
|
193
|
+
onClick={() => setShowSecretValue((prev) => !prev)}
|
|
194
|
+
aria-label={showSecretValue ? 'Hide password' : 'Show password'}
|
|
195
|
+
>
|
|
196
|
+
{showSecretValue ? (
|
|
197
|
+
<EyeOff className="h-4 w-4" />
|
|
198
|
+
) : (
|
|
199
|
+
<Eye className="h-4 w-4" />
|
|
200
|
+
)}
|
|
201
|
+
</Button>
|
|
202
|
+
</div>
|
|
183
203
|
);
|
|
184
204
|
|
|
185
205
|
case SettingComponentEnum.INPUT_FILE:
|
|
@@ -242,17 +262,17 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
242
262
|
};
|
|
243
263
|
|
|
244
264
|
return (
|
|
245
|
-
<div className="flex flex-col gap-2 min-w-
|
|
265
|
+
<div className="flex w-full min-w-0 flex-col gap-2 md:min-w-50">
|
|
246
266
|
{parsedValue && (
|
|
247
267
|
<div>
|
|
248
268
|
<img
|
|
249
269
|
src={parsedValue || ''}
|
|
250
270
|
alt={t('uploadedFileAlt')}
|
|
251
|
-
className="w-
|
|
271
|
+
className="max-w-full md:w-50"
|
|
252
272
|
/>
|
|
253
273
|
</div>
|
|
254
274
|
)}
|
|
255
|
-
<div className="flex items-center gap-2">
|
|
275
|
+
<div className="flex w-full min-w-0 items-center gap-2">
|
|
256
276
|
<input
|
|
257
277
|
ref={fileInputRef}
|
|
258
278
|
type="file"
|
|
@@ -274,7 +294,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
274
294
|
type="text"
|
|
275
295
|
value={parsedValue || ''}
|
|
276
296
|
onChange={(e) => handleChangeValue(formatValue(e.target.value))}
|
|
277
|
-
className="flex-1 bg-background
|
|
297
|
+
className="min-w-0 flex-1 bg-background"
|
|
278
298
|
placeholder={t('fileUrlPlaceholder')}
|
|
279
299
|
disabled={isUploading}
|
|
280
300
|
/>
|
|
@@ -301,7 +321,10 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
301
321
|
: [];
|
|
302
322
|
return (
|
|
303
323
|
<div
|
|
304
|
-
className={cn(
|
|
324
|
+
className={cn(
|
|
325
|
+
'flex w-full min-w-0 flex-col space-y-2 md:min-w-50',
|
|
326
|
+
className
|
|
327
|
+
)}
|
|
305
328
|
>
|
|
306
329
|
{options.map((option) => (
|
|
307
330
|
<div key={option.id} className="flex items-center space-x-2">
|
|
@@ -332,7 +355,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
332
355
|
onCheckedChange={(checked) =>
|
|
333
356
|
handleChangeValue(formatValue(checked), true)
|
|
334
357
|
}
|
|
335
|
-
className={cn(className
|
|
358
|
+
className={cn(className)}
|
|
336
359
|
/>
|
|
337
360
|
);
|
|
338
361
|
|
|
@@ -344,7 +367,10 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
344
367
|
onValueChange={(val) => handleChangeValue(formatValue(val), true)}
|
|
345
368
|
>
|
|
346
369
|
<SelectTrigger
|
|
347
|
-
className={cn(
|
|
370
|
+
className={cn(
|
|
371
|
+
'w-full min-w-0 bg-background md:min-w-50',
|
|
372
|
+
className
|
|
373
|
+
)}
|
|
348
374
|
>
|
|
349
375
|
<SelectValue placeholder={t('selectOption')} />
|
|
350
376
|
</SelectTrigger>
|
|
@@ -364,7 +390,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
364
390
|
<RadioGroup
|
|
365
391
|
value={parsedValue?.toString() || ''}
|
|
366
392
|
onValueChange={(val) => handleChangeValue(formatValue(val), true)}
|
|
367
|
-
className={cn(className, 'min-w-
|
|
393
|
+
className={cn(className, 'w-full min-w-0 md:min-w-50')}
|
|
368
394
|
>
|
|
369
395
|
{radioOptions.map((option) => (
|
|
370
396
|
<div key={option.id} className="flex items-center space-x-2">
|
|
@@ -382,7 +408,7 @@ export const SettingField = ({ setting, className }: SettingFieldProps) => {
|
|
|
382
408
|
case SettingComponentEnum.COLOR_PICKER:
|
|
383
409
|
isTextInputRef.current = true;
|
|
384
410
|
return (
|
|
385
|
-
<div className="flex items-center gap-2
|
|
411
|
+
<div className="flex w-full min-w-0 items-center gap-2 md:max-w-50">
|
|
386
412
|
<Input
|
|
387
413
|
type="color"
|
|
388
414
|
value={parsedValue || '#000000'}
|
|
@@ -1,13 +1,43 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Card,
|
|
5
|
+
CardContent,
|
|
6
|
+
CardDescription,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
} from '@/components/ui/card';
|
|
4
10
|
import { PaginatedResult } from '@/types/pagination-result';
|
|
5
|
-
import { Setting } from '@hed-hog/api-types';
|
|
11
|
+
import { Setting, SettingSubgroup } from '@hed-hog/api-types';
|
|
6
12
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
7
13
|
import { Loader } from 'lucide-react';
|
|
8
14
|
import { useParams } from 'next/navigation';
|
|
15
|
+
import { useMemo } from 'react';
|
|
9
16
|
import { SettingField } from './components/setting-field';
|
|
10
17
|
|
|
18
|
+
type SettingSection = {
|
|
19
|
+
key: string;
|
|
20
|
+
subgroupId: number | null;
|
|
21
|
+
subgroup: SettingSubgroup | null;
|
|
22
|
+
settings: Setting[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type SettingWithLegacySubgroup = Setting & {
|
|
26
|
+
subgroup_id?: number | null;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const getLocalizedValue = (
|
|
30
|
+
value?: string | Record<string, string>
|
|
31
|
+
): string | undefined => {
|
|
32
|
+
if (!value) return undefined;
|
|
33
|
+
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return value.en || Object.values(value)[0];
|
|
39
|
+
};
|
|
40
|
+
|
|
11
41
|
export default function Page() {
|
|
12
42
|
const params = useParams();
|
|
13
43
|
const slug = params?.slug;
|
|
@@ -23,39 +53,116 @@ export default function Page() {
|
|
|
23
53
|
},
|
|
24
54
|
});
|
|
25
55
|
|
|
56
|
+
const groupedSections = useMemo<SettingSection[]>(() => {
|
|
57
|
+
const sectionsMap = new Map<string, SettingSection>();
|
|
58
|
+
|
|
59
|
+
for (const setting of (settings?.data ||
|
|
60
|
+
[]) as SettingWithLegacySubgroup[]) {
|
|
61
|
+
const subgroupId =
|
|
62
|
+
setting.subgroupId ??
|
|
63
|
+
setting.subgroup_id ??
|
|
64
|
+
setting.setting_subgroup?.id ??
|
|
65
|
+
null;
|
|
66
|
+
const subgroup = setting.setting_subgroup ?? null;
|
|
67
|
+
const sectionKey =
|
|
68
|
+
subgroupId === null ? 'ungrouped' : `subgroup-${subgroupId}`;
|
|
69
|
+
|
|
70
|
+
if (!sectionsMap.has(sectionKey)) {
|
|
71
|
+
sectionsMap.set(sectionKey, {
|
|
72
|
+
key: sectionKey,
|
|
73
|
+
subgroupId,
|
|
74
|
+
subgroup,
|
|
75
|
+
settings: [],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
sectionsMap.get(sectionKey)?.settings.push(setting);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const sections = Array.from(sectionsMap.values());
|
|
83
|
+
|
|
84
|
+
sections.forEach((section) => {
|
|
85
|
+
section.settings.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
sections.sort((a, b) => {
|
|
89
|
+
if (a.subgroupId === null && b.subgroupId !== null) return 1;
|
|
90
|
+
if (a.subgroupId !== null && b.subgroupId === null) return -1;
|
|
91
|
+
|
|
92
|
+
const aSlug = a.subgroup?.slug || '';
|
|
93
|
+
const bSlug = b.subgroup?.slug || '';
|
|
94
|
+
|
|
95
|
+
const slugSort = aSlug.localeCompare(bSlug);
|
|
96
|
+
if (slugSort !== 0) return slugSort;
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
(a.subgroupId ?? Number.MAX_SAFE_INTEGER) -
|
|
100
|
+
(b.subgroupId ?? Number.MAX_SAFE_INTEGER)
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return sections;
|
|
105
|
+
}, [settings?.data]);
|
|
106
|
+
|
|
26
107
|
if (!slug) {
|
|
27
108
|
return <></>;
|
|
28
109
|
}
|
|
29
110
|
|
|
30
111
|
return (
|
|
31
|
-
<div className="space-y-6">
|
|
32
|
-
<form className="space-y-
|
|
33
|
-
|
|
34
|
-
|
|
112
|
+
<div className="min-w-0 space-y-6">
|
|
113
|
+
<form className="min-w-0 space-y-6">
|
|
114
|
+
{isLoading && (
|
|
115
|
+
<Card className="border-none bg-accent">
|
|
35
116
|
<CardContent className="flex h-32 w-full items-center justify-center">
|
|
36
117
|
<Loader className="animate-spin text-muted-foreground w-4 h-4" />
|
|
37
118
|
</CardContent>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
119
|
+
</Card>
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
{!isLoading &&
|
|
123
|
+
groupedSections.map((section) => {
|
|
124
|
+
const subgroupName = getLocalizedValue(section.subgroup?.name);
|
|
125
|
+
const subgroupDescription = getLocalizedValue(
|
|
126
|
+
section.subgroup?.description
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Card key={section.key} className="min-w-0 border-none bg-accent">
|
|
131
|
+
{section.subgroup && (
|
|
132
|
+
<CardHeader className="pb-3">
|
|
133
|
+
<CardTitle>
|
|
134
|
+
{subgroupName || section.subgroup.slug}
|
|
135
|
+
</CardTitle>
|
|
136
|
+
{subgroupDescription && (
|
|
137
|
+
<CardDescription>{subgroupDescription}</CardDescription>
|
|
138
|
+
)}
|
|
139
|
+
</CardHeader>
|
|
140
|
+
)}
|
|
141
|
+
|
|
142
|
+
<CardContent className="min-w-0 space-y-4">
|
|
143
|
+
{section.settings.map((setting: Setting) => (
|
|
144
|
+
<div
|
|
145
|
+
key={setting.id}
|
|
146
|
+
className="flex min-w-0 flex-col gap-3 border-b border-border pb-4 last:border-0 last:pb-0 md:flex-row md:items-center md:justify-between md:gap-4"
|
|
147
|
+
>
|
|
148
|
+
<div className="min-w-0 flex-1">
|
|
149
|
+
<label className="block font-semibold">
|
|
150
|
+
{setting.name}
|
|
151
|
+
</label>
|
|
152
|
+
<p className="text-muted-foreground text-sm wrap-break-word">
|
|
153
|
+
{setting.description}
|
|
154
|
+
</p>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div className="flex w-full min-w-0 items-center md:h-full md:w-auto md:max-w-full md:justify-end">
|
|
158
|
+
<SettingField setting={setting} />
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
))}
|
|
162
|
+
</CardContent>
|
|
163
|
+
</Card>
|
|
164
|
+
);
|
|
165
|
+
})}
|
|
59
166
|
</form>
|
|
60
167
|
</div>
|
|
61
168
|
);
|