@devstroupe/devkit-cli 1.0.0

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 (170) hide show
  1. package/README.md +40 -0
  2. package/dist/boilerplates/angular-template/.dockerignore +4 -0
  3. package/dist/boilerplates/angular-template/.postcssrc.json +5 -0
  4. package/dist/boilerplates/angular-template/Dockerfile +14 -0
  5. package/dist/boilerplates/angular-template/angular.json +85 -0
  6. package/dist/boilerplates/angular-template/components.json +5 -0
  7. package/dist/boilerplates/angular-template/framework.code-workspace +7 -0
  8. package/dist/boilerplates/angular-template/nginx.conf +23 -0
  9. package/dist/boilerplates/angular-template/node_modules/.bin/browserslist +21 -0
  10. package/dist/boilerplates/angular-template/node_modules/.bin/jiti +21 -0
  11. package/dist/boilerplates/angular-template/node_modules/.bin/lessc +21 -0
  12. package/dist/boilerplates/angular-template/node_modules/.bin/ng +21 -0
  13. package/dist/boilerplates/angular-template/node_modules/.bin/ng-xi18n +21 -0
  14. package/dist/boilerplates/angular-template/node_modules/.bin/ngc +21 -0
  15. package/dist/boilerplates/angular-template/node_modules/.bin/sass +21 -0
  16. package/dist/boilerplates/angular-template/node_modules/.bin/terser +21 -0
  17. package/dist/boilerplates/angular-template/node_modules/.bin/tsc +21 -0
  18. package/dist/boilerplates/angular-template/node_modules/.bin/tsserver +21 -0
  19. package/dist/boilerplates/angular-template/node_modules/.bin/vite +21 -0
  20. package/dist/boilerplates/angular-template/node_modules/.bin/vitest +21 -0
  21. package/dist/boilerplates/angular-template/node_modules/.bin/yaml +21 -0
  22. package/dist/boilerplates/angular-template/package.json +47 -0
  23. package/dist/boilerplates/angular-template/postcss.config.js +5 -0
  24. package/dist/boilerplates/angular-template/proxy.conf.json +7 -0
  25. package/dist/boilerplates/angular-template/src/app/app.component.html +3 -0
  26. package/dist/boilerplates/angular-template/src/app/app.component.ts +12 -0
  27. package/dist/boilerplates/angular-template/src/app/app.config.ts +59 -0
  28. package/dist/boilerplates/angular-template/src/app/app.entity-routes.ts +4 -0
  29. package/dist/boilerplates/angular-template/src/app/app.routes.ts +25 -0
  30. package/dist/boilerplates/angular-template/src/app/core/guards/auth.guard.ts +16 -0
  31. package/dist/boilerplates/angular-template/src/app/core/services/auth.service.ts +98 -0
  32. package/dist/boilerplates/angular-template/src/app/modules/auth/login/login.component.html +51 -0
  33. package/dist/boilerplates/angular-template/src/app/modules/auth/login/login.component.ts +47 -0
  34. package/dist/boilerplates/angular-template/src/app/modules/auth/register/register.component.html +59 -0
  35. package/dist/boilerplates/angular-template/src/app/modules/auth/register/register.component.ts +47 -0
  36. package/dist/boilerplates/angular-template/src/app/modules/dashboard/dashboard.component.html +14 -0
  37. package/dist/boilerplates/angular-template/src/app/modules/dashboard/dashboard.component.ts +23 -0
  38. package/dist/boilerplates/angular-template/src/app/shared/interceptors/tenant.interceptor.ts +25 -0
  39. package/dist/boilerplates/angular-template/src/index.html +64 -0
  40. package/dist/boilerplates/angular-template/src/main.ts +6 -0
  41. package/dist/boilerplates/angular-template/src/styles.css +9 -0
  42. package/dist/boilerplates/angular-template/tsconfig.json +36 -0
  43. package/dist/boilerplates/nest-template/Dockerfile +16 -0
  44. package/dist/boilerplates/nest-template/node_modules/.bin/acorn +21 -0
  45. package/dist/boilerplates/nest-template/node_modules/.bin/nest +21 -0
  46. package/dist/boilerplates/nest-template/node_modules/.bin/prettier +21 -0
  47. package/dist/boilerplates/nest-template/node_modules/.bin/tsc +21 -0
  48. package/dist/boilerplates/nest-template/node_modules/.bin/tsserver +21 -0
  49. package/dist/boilerplates/nest-template/node_modules/.bin/typeorm +21 -0
  50. package/dist/boilerplates/nest-template/node_modules/.bin/typeorm-ts-node-commonjs +21 -0
  51. package/dist/boilerplates/nest-template/node_modules/.bin/typeorm-ts-node-esm +21 -0
  52. package/dist/boilerplates/nest-template/node_modules/.bin/webpack +21 -0
  53. package/dist/boilerplates/nest-template/package.json +43 -0
  54. package/dist/boilerplates/nest-template/src/app.module.ts +24 -0
  55. package/dist/boilerplates/nest-template/src/database/data-source.ts +16 -0
  56. package/dist/boilerplates/nest-template/src/database/migrations/1700000000000-create-users.ts +26 -0
  57. package/dist/boilerplates/nest-template/src/main.ts +46 -0
  58. package/dist/boilerplates/nest-template/src/modules/auth/auth.controller.ts +34 -0
  59. package/dist/boilerplates/nest-template/src/modules/auth/auth.module.ts +22 -0
  60. package/dist/boilerplates/nest-template/src/modules/auth/auth.service.ts +57 -0
  61. package/dist/boilerplates/nest-template/src/modules/auth/current-user.decorator.ts +8 -0
  62. package/dist/boilerplates/nest-template/src/modules/auth/jwt-auth.guard.ts +31 -0
  63. package/dist/boilerplates/nest-template/src/modules/auth/roles.decorator.ts +4 -0
  64. package/dist/boilerplates/nest-template/src/modules/auth/roles.guard.ts +24 -0
  65. package/dist/boilerplates/nest-template/src/modules/user/database-seed.service.ts +47 -0
  66. package/dist/boilerplates/nest-template/src/modules/user/domain/user.repository.ts +9 -0
  67. package/dist/boilerplates/nest-template/src/modules/user/domain/user.ts +9 -0
  68. package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.orm-entity.ts +31 -0
  69. package/dist/boilerplates/nest-template/src/modules/user/infra/database/user.repository.adapter.ts +53 -0
  70. package/dist/boilerplates/nest-template/src/modules/user/service/user.service.ts +44 -0
  71. package/dist/boilerplates/nest-template/src/modules/user/user.module.ts +20 -0
  72. package/dist/boilerplates/nest-template/tsconfig.build.json +4 -0
  73. package/dist/boilerplates/nest-template/tsconfig.json +21 -0
  74. package/dist/index.d.ts +2 -0
  75. package/dist/index.js +1967 -0
  76. package/dist/migrations/sort-entities.d.ts +2 -0
  77. package/dist/migrations/sort-entities.js +30 -0
  78. package/dist/rules/linter-rules.d.ts +12 -0
  79. package/dist/rules/linter-rules.js +338 -0
  80. package/dist/rules/linter-rules.test.d.ts +1 -0
  81. package/dist/rules/linter-rules.test.js +214 -0
  82. package/dist/templates/angular/dialog-handler.template.d.ts +2 -0
  83. package/dist/templates/angular/dialog-handler.template.js +49 -0
  84. package/dist/templates/angular/form.template.d.ts +3 -0
  85. package/dist/templates/angular/form.template.js +453 -0
  86. package/dist/templates/angular/index.d.ts +4 -0
  87. package/dist/templates/angular/index.js +20 -0
  88. package/dist/templates/angular/list.template.d.ts +3 -0
  89. package/dist/templates/angular/list.template.js +213 -0
  90. package/dist/templates/angular/service.template.d.ts +1 -0
  91. package/dist/templates/angular/service.template.js +20 -0
  92. package/dist/templates/cli-templates.d.ts +2 -0
  93. package/dist/templates/cli-templates.js +18 -0
  94. package/dist/templates/nest/crud.templates.d.ts +9 -0
  95. package/dist/templates/nest/crud.templates.js +362 -0
  96. package/dist/templates/nest/index.d.ts +3 -0
  97. package/dist/templates/nest/index.js +19 -0
  98. package/dist/templates/nest/microservice.templates.d.ts +4 -0
  99. package/dist/templates/nest/microservice.templates.js +157 -0
  100. package/dist/templates/nest/migration.template.d.ts +3 -0
  101. package/dist/templates/nest/migration.template.js +127 -0
  102. package/dist/templates/relationship-templates.test.d.ts +1 -0
  103. package/dist/templates/relationship-templates.test.js +181 -0
  104. package/dist/templates/shared/names.d.ts +3 -0
  105. package/dist/templates/shared/names.js +14 -0
  106. package/dist/templates/shared/relationships.d.ts +27 -0
  107. package/dist/templates/shared/relationships.js +96 -0
  108. package/dist/templates/ui/components/avatar.template.d.ts +3 -0
  109. package/dist/templates/ui/components/avatar.template.js +66 -0
  110. package/dist/templates/ui/components/badge.template.d.ts +3 -0
  111. package/dist/templates/ui/components/badge.template.js +27 -0
  112. package/dist/templates/ui/components/button.template.d.ts +5 -0
  113. package/dist/templates/ui/components/button.template.js +64 -0
  114. package/dist/templates/ui/components/card-list.template.d.ts +5 -0
  115. package/dist/templates/ui/components/card-list.template.js +79 -0
  116. package/dist/templates/ui/components/dialog.template.d.ts +5 -0
  117. package/dist/templates/ui/components/dialog.template.js +51 -0
  118. package/dist/templates/ui/components/filter.template.d.ts +4 -0
  119. package/dist/templates/ui/components/filter.template.js +218 -0
  120. package/dist/templates/ui/components/index.d.ts +10 -0
  121. package/dist/templates/ui/components/index.js +26 -0
  122. package/dist/templates/ui/components/input.template.d.ts +4 -0
  123. package/dist/templates/ui/components/input.template.js +76 -0
  124. package/dist/templates/ui/components/select.template.d.ts +4 -0
  125. package/dist/templates/ui/components/select.template.js +176 -0
  126. package/dist/templates/ui/components/simple-list.template.d.ts +5 -0
  127. package/dist/templates/ui/components/simple-list.template.js +89 -0
  128. package/dist/templates/ui/components/table.template.d.ts +5 -0
  129. package/dist/templates/ui/components/table.template.js +112 -0
  130. package/dist/templates/ui/index.d.ts +9 -0
  131. package/dist/templates/ui/index.js +25 -0
  132. package/dist/templates/ui/layout/header.template.d.ts +5 -0
  133. package/dist/templates/ui/layout/header.template.js +236 -0
  134. package/dist/templates/ui/layout/index.d.ts +4 -0
  135. package/dist/templates/ui/layout/index.js +20 -0
  136. package/dist/templates/ui/layout/main-layout.template.d.ts +5 -0
  137. package/dist/templates/ui/layout/main-layout.template.js +126 -0
  138. package/dist/templates/ui/layout/shell.template.d.ts +5 -0
  139. package/dist/templates/ui/layout/shell.template.js +76 -0
  140. package/dist/templates/ui/layout/sidebar.template.d.ts +5 -0
  141. package/dist/templates/ui/layout/sidebar.template.js +412 -0
  142. package/dist/templates/ui/playground.template.d.ts +5 -0
  143. package/dist/templates/ui/playground.template.js +570 -0
  144. package/dist/templates/ui/readme.template.d.ts +1 -0
  145. package/dist/templates/ui/readme.template.js +23 -0
  146. package/dist/templates/ui/shared/colors.d.ts +1 -0
  147. package/dist/templates/ui/shared/colors.js +31 -0
  148. package/dist/templates/ui/shared/profile-component.template.d.ts +5 -0
  149. package/dist/templates/ui/shared/profile-component.template.js +403 -0
  150. package/dist/templates/ui/shared/profile-service.template.d.ts +1 -0
  151. package/dist/templates/ui/shared/profile-service.template.js +44 -0
  152. package/dist/templates/ui/shared/theme-service.template.d.ts +1 -0
  153. package/dist/templates/ui/shared/theme-service.template.js +47 -0
  154. package/dist/templates/ui/spartan/badge-directive.template.d.ts +3 -0
  155. package/dist/templates/ui/spartan/badge-directive.template.js +50 -0
  156. package/dist/templates/ui/spartan/button-directive.template.d.ts +3 -0
  157. package/dist/templates/ui/spartan/button-directive.template.js +82 -0
  158. package/dist/templates/ui/spartan/button-token.template.d.ts +3 -0
  159. package/dist/templates/ui/spartan/button-token.template.js +31 -0
  160. package/dist/templates/ui/spartan/hlm-core.template.d.ts +3 -0
  161. package/dist/templates/ui/spartan/hlm-core.template.js +322 -0
  162. package/dist/templates/ui/spartan/index.d.ts +5 -0
  163. package/dist/templates/ui/spartan/index.js +21 -0
  164. package/dist/templates/ui/spartan/input-directive.template.d.ts +3 -0
  165. package/dist/templates/ui/spartan/input-directive.template.js +31 -0
  166. package/dist/templates/ui/theme-css.template.d.ts +7 -0
  167. package/dist/templates/ui/theme-css.template.js +210 -0
  168. package/dist/templates/ui-templates.d.ts +1 -0
  169. package/dist/templates/ui-templates.js +17 -0
  170. package/package.json +50 -0
@@ -0,0 +1,403 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devkitProfileComponentTemplate = devkitProfileComponentTemplate;
4
+ function devkitProfileComponentTemplate() {
5
+ return {
6
+ ts: `import { Component, OnInit, inject } from '@angular/core';
7
+ import { CommonModule } from '@angular/common';
8
+ import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
9
+ import { Router } from '@angular/router';
10
+ import { NgIconComponent } from '@ng-icons/core';
11
+ import { HlmButton } from '@spartan-ng/helm/button';
12
+ import { HlmInput } from '@spartan-ng/helm/input';
13
+ import { AuthService } from '../../../core/services/auth.service';
14
+ import { ProfileService } from '../../../core/services/profile.service';
15
+
16
+ @Component({
17
+ selector: 'app-profile',
18
+ standalone: true,
19
+ imports: [CommonModule, ReactiveFormsModule, NgIconComponent, HlmButton, HlmInput],
20
+ templateUrl: './profile.component.html',
21
+ styleUrl: './profile.component.css'
22
+ })
23
+ export class ProfileComponent implements OnInit {
24
+ private fb = inject(FormBuilder);
25
+ private authService = inject(AuthService);
26
+ private profileService = inject(ProfileService);
27
+ private router = inject(Router);
28
+
29
+ profileForm!: FormGroup;
30
+ isLoading = false;
31
+ isSaving = false;
32
+ successMessage = '';
33
+ errorMessage = '';
34
+ avatarPreview = '';
35
+
36
+ currentUser$ = this.authService.currentUser$;
37
+
38
+ ngOnInit(): void {
39
+ const user = this.authService.currentUser;
40
+ this.profileForm = this.fb.group({
41
+ name: [user?.name || '', [Validators.required, Validators.minLength(2)]],
42
+ email: [user?.email || '', [Validators.required, Validators.email]],
43
+ avatar: [user?.avatar || '']
44
+ });
45
+ this.avatarPreview = user?.avatar || '';
46
+ }
47
+
48
+ onAvatarChange(): void {
49
+ const url = this.profileForm.get('avatar')?.value;
50
+ this.avatarPreview = url || '';
51
+ }
52
+
53
+ onSave(): void {
54
+ if (this.profileForm.invalid) {
55
+ this.profileForm.markAllAsTouched();
56
+ return;
57
+ }
58
+
59
+ this.isSaving = true;
60
+ this.successMessage = '';
61
+ this.errorMessage = '';
62
+
63
+ const data = this.profileForm.value;
64
+
65
+ this.profileService.updateProfile(data).subscribe({
66
+ next: () => {
67
+ this.isSaving = false;
68
+ this.successMessage = 'Perfil atualizado com sucesso!';
69
+ setTimeout(() => (this.successMessage = ''), 4000);
70
+ },
71
+ error: (err) => {
72
+ this.isSaving = false;
73
+ this.errorMessage = err?.error?.message || 'Erro ao salvar o perfil. Tente novamente.';
74
+ }
75
+ });
76
+ }
77
+
78
+ onCancel(): void {
79
+ this.router.navigate(['/dashboard']);
80
+ }
81
+
82
+ get nameControl() { return this.profileForm.get('name'); }
83
+ get emailControl() { return this.profileForm.get('email'); }
84
+ }
85
+ `,
86
+ html: `<div class="dt-profile-page">
87
+ <div class="dt-profile-container">
88
+
89
+ <!-- Header da Página -->
90
+ <div class="dt-profile-header">
91
+ <button hlmBtn variant="ghost" size="icon" (click)="onCancel()" class="dt-profile-back" title="Voltar">
92
+ <ng-icon name="lucideArrowLeft" class="size-5"></ng-icon>
93
+ </button>
94
+ <div>
95
+ <h1 class="dt-profile-title">Editar Perfil</h1>
96
+ <p class="dt-profile-subtitle">Atualize suas informações pessoais</p>
97
+ </div>
98
+ </div>
99
+
100
+ <div class="dt-profile-content" *ngIf="currentUser$ | async as user">
101
+
102
+ <!-- Card do Avatar -->
103
+ <div class="dt-profile-avatar-card">
104
+ <div class="dt-profile-avatar-wrapper">
105
+ <img
106
+ *ngIf="avatarPreview; else avatarInitials"
107
+ [src]="avatarPreview"
108
+ [alt]="user.name"
109
+ class="dt-profile-avatar-img"
110
+ (error)="avatarPreview = ''"
111
+ />
112
+ <ng-template #avatarInitials>
113
+ <div class="dt-profile-avatar-initials">
114
+ {{ user.name.slice(0, 2).toUpperCase() }}
115
+ </div>
116
+ </ng-template>
117
+ </div>
118
+ <div class="dt-profile-avatar-info">
119
+ <p class="dt-profile-avatar-name">{{ user.name }}</p>
120
+ <p class="dt-profile-avatar-role" *ngIf="user.role">{{ user.role }}</p>
121
+ </div>
122
+ </div>
123
+
124
+ <!-- Formulário -->
125
+ <form [formGroup]="profileForm" (ngSubmit)="onSave()" class="dt-profile-form" novalidate>
126
+
127
+ <!-- Campo: Nome -->
128
+ <div class="dt-profile-field">
129
+ <label class="dt-profile-label" for="profile-name">
130
+ <ng-icon name="lucideUser" class="size-4"></ng-icon>
131
+ Nome Completo
132
+ </label>
133
+ <input
134
+ hlmInput
135
+ id="profile-name"
136
+ type="text"
137
+ formControlName="name"
138
+ placeholder="Seu nome completo"
139
+ class="dt-profile-input"
140
+ [class.dt-profile-input--error]="nameControl?.invalid && nameControl?.touched"
141
+ />
142
+ <span *ngIf="nameControl?.invalid && nameControl?.touched" class="dt-profile-error">
143
+ {{ nameControl?.errors?.['required'] ? 'Nome é obrigatório.' : 'Nome deve ter no mínimo 2 caracteres.' }}
144
+ </span>
145
+ </div>
146
+
147
+ <!-- Campo: E-mail -->
148
+ <div class="dt-profile-field">
149
+ <label class="dt-profile-label" for="profile-email">
150
+ <ng-icon name="lucideMail" class="size-4"></ng-icon>
151
+ E-mail
152
+ </label>
153
+ <input
154
+ hlmInput
155
+ id="profile-email"
156
+ type="email"
157
+ formControlName="email"
158
+ placeholder="seu@email.com"
159
+ class="dt-profile-input"
160
+ [class.dt-profile-input--error]="emailControl?.invalid && emailControl?.touched"
161
+ />
162
+ <span *ngIf="emailControl?.invalid && emailControl?.touched" class="dt-profile-error">
163
+ {{ emailControl?.errors?.['required'] ? 'E-mail é obrigatório.' : 'Informe um e-mail válido.' }}
164
+ </span>
165
+ </div>
166
+
167
+ <!-- Campo: Avatar URL -->
168
+ <div class="dt-profile-field">
169
+ <label class="dt-profile-label" for="profile-avatar">
170
+ <ng-icon name="lucideImage" class="size-4"></ng-icon>
171
+ URL do Avatar (opcional)
172
+ </label>
173
+ <input
174
+ hlmInput
175
+ id="profile-avatar"
176
+ type="url"
177
+ formControlName="avatar"
178
+ placeholder="https://exemplo.com/sua-foto.jpg"
179
+ class="dt-profile-input"
180
+ (input)="onAvatarChange()"
181
+ />
182
+ <span class="dt-profile-hint">Cole a URL de uma imagem para usar como foto de perfil.</span>
183
+ </div>
184
+
185
+ <!-- Feedbacks -->
186
+ <div *ngIf="successMessage" class="dt-profile-alert dt-profile-alert--success">
187
+ <ng-icon name="lucideCircleCheck" class="size-4"></ng-icon>
188
+ {{ successMessage }}
189
+ </div>
190
+
191
+ <div *ngIf="errorMessage" class="dt-profile-alert dt-profile-alert--error">
192
+ <ng-icon name="lucideCircleAlert" class="size-4"></ng-icon>
193
+ {{ errorMessage }}
194
+ </div>
195
+
196
+ <!-- Ações -->
197
+ <div class="dt-profile-actions">
198
+ <button hlmBtn type="button" variant="outline" (click)="onCancel()" [disabled]="isSaving">
199
+ Cancelar
200
+ </button>
201
+ <button hlmBtn type="submit" variant="default" [disabled]="isSaving || profileForm.invalid">
202
+ <ng-icon *ngIf="isSaving" name="lucideLoader" class="size-4 mr-2 dt-spin"></ng-icon>
203
+ {{ isSaving ? 'Salvando...' : 'Salvar Alterações' }}
204
+ </button>
205
+ </div>
206
+
207
+ </form>
208
+ </div>
209
+
210
+ </div>
211
+ </div>
212
+ `,
213
+ css: `.dt-profile-page {
214
+ min-height: 100%;
215
+ padding: 32px 16px;
216
+ background-color: var(--background);
217
+ font-family: var(--font-family);
218
+ }
219
+
220
+ .dt-profile-container {
221
+ max-width: 640px;
222
+ margin: 0 auto;
223
+ display: flex;
224
+ flex-direction: column;
225
+ gap: 28px;
226
+ }
227
+
228
+ /* Header */
229
+ .dt-profile-header {
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 12px;
233
+ }
234
+
235
+ .dt-profile-back {
236
+ flex-shrink: 0;
237
+ }
238
+
239
+ .dt-profile-title {
240
+ font-size: 22px;
241
+ font-weight: 700;
242
+ color: var(--foreground);
243
+ margin: 0;
244
+ }
245
+
246
+ .dt-profile-subtitle {
247
+ font-size: 13px;
248
+ color: var(--muted-foreground);
249
+ margin: 2px 0 0;
250
+ }
251
+
252
+ /* Card Avatar */
253
+ .dt-profile-avatar-card {
254
+ display: flex;
255
+ align-items: center;
256
+ gap: 20px;
257
+ padding: 24px;
258
+ background-color: var(--card);
259
+ border: 1px solid var(--border);
260
+ border-radius: var(--radius);
261
+ }
262
+
263
+ .dt-profile-avatar-wrapper {
264
+ flex-shrink: 0;
265
+ }
266
+
267
+ .dt-profile-avatar-img {
268
+ width: 80px;
269
+ height: 80px;
270
+ border-radius: 50%;
271
+ object-fit: cover;
272
+ border: 3px solid var(--border);
273
+ }
274
+
275
+ .dt-profile-avatar-initials {
276
+ width: 80px;
277
+ height: 80px;
278
+ border-radius: 50%;
279
+ display: flex;
280
+ align-items: center;
281
+ justify-content: center;
282
+ font-size: 24px;
283
+ font-weight: 700;
284
+ background-color: var(--primary);
285
+ color: var(--primary-foreground);
286
+ border: 3px solid color-mix(in srgb, var(--primary) 30%, transparent);
287
+ }
288
+
289
+ .dt-profile-avatar-name {
290
+ font-size: 18px;
291
+ font-weight: 600;
292
+ color: var(--foreground);
293
+ margin: 0;
294
+ }
295
+
296
+ .dt-profile-avatar-role {
297
+ font-size: 13px;
298
+ color: var(--muted-foreground);
299
+ margin: 4px 0 0;
300
+ }
301
+
302
+ /* Formulário */
303
+ .dt-profile-form {
304
+ display: flex;
305
+ flex-direction: column;
306
+ gap: 20px;
307
+ padding: 24px;
308
+ background-color: var(--card);
309
+ border: 1px solid var(--border);
310
+ border-radius: var(--radius);
311
+ }
312
+
313
+ .dt-profile-field {
314
+ display: flex;
315
+ flex-direction: column;
316
+ gap: 6px;
317
+ }
318
+
319
+ .dt-profile-label {
320
+ font-size: 13px;
321
+ font-weight: 600;
322
+ color: var(--foreground);
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 6px;
326
+ }
327
+
328
+ .dt-profile-input {
329
+ width: 100%;
330
+ }
331
+
332
+ .dt-profile-input--error {
333
+ border-color: var(--destructive) !important;
334
+ }
335
+
336
+ .dt-profile-error {
337
+ font-size: 12px;
338
+ color: var(--destructive);
339
+ }
340
+
341
+ .dt-profile-hint {
342
+ font-size: 12px;
343
+ color: var(--muted-foreground);
344
+ }
345
+
346
+ /* Feedbacks */
347
+ .dt-profile-alert {
348
+ display: flex;
349
+ align-items: center;
350
+ gap: 8px;
351
+ padding: 12px 16px;
352
+ border-radius: var(--radius);
353
+ font-size: 13px;
354
+ font-weight: 500;
355
+ }
356
+
357
+ .dt-profile-alert--success {
358
+ background-color: color-mix(in srgb, var(--primary) 10%, transparent);
359
+ color: var(--primary);
360
+ border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
361
+ }
362
+
363
+ .dt-profile-alert--error {
364
+ background-color: color-mix(in srgb, var(--destructive) 10%, transparent);
365
+ color: var(--destructive);
366
+ border: 1px solid color-mix(in srgb, var(--destructive) 30%, transparent);
367
+ }
368
+
369
+ /* Ações */
370
+ .dt-profile-actions {
371
+ display: flex;
372
+ justify-content: flex-end;
373
+ gap: 12px;
374
+ padding-top: 8px;
375
+ border-top: 1px solid var(--border);
376
+ }
377
+
378
+ /* Spinner */
379
+ @keyframes dt-spin {
380
+ from { transform: rotate(0deg); }
381
+ to { transform: rotate(360deg); }
382
+ }
383
+
384
+ .dt-spin {
385
+ animation: dt-spin 1s linear infinite;
386
+ }
387
+
388
+ @media (max-width: 640px) {
389
+ .dt-profile-page {
390
+ padding: 16px 12px;
391
+ }
392
+
393
+ .dt-profile-actions {
394
+ flex-direction: column-reverse;
395
+ }
396
+
397
+ .dt-profile-actions button {
398
+ width: 100%;
399
+ }
400
+ }
401
+ `
402
+ };
403
+ }
@@ -0,0 +1 @@
1
+ export declare function devkitProfileServiceTemplate(): string;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devkitProfileServiceTemplate = devkitProfileServiceTemplate;
4
+ function devkitProfileServiceTemplate() {
5
+ return `import { Injectable, inject } from '@angular/core';
6
+ import { HttpClient } from '@angular/common/http';
7
+ import { Observable, tap } from 'rxjs';
8
+ import { AuthService } from './auth.service';
9
+
10
+ export interface UpdateProfileDto {
11
+ name?: string;
12
+ email?: string;
13
+ avatar?: string;
14
+ }
15
+
16
+ @Injectable({
17
+ providedIn: 'root'
18
+ })
19
+ export class ProfileService {
20
+ private http = inject(HttpClient);
21
+ private authService = inject(AuthService);
22
+ private apiUrl = '/api/auth/profile';
23
+
24
+ updateProfile(data: UpdateProfileDto): Observable<any> {
25
+ return this.http.patch<any>(this.apiUrl, data).pipe(
26
+ tap((updatedUser) => {
27
+ const current = this.authService.currentUser;
28
+ if (current) {
29
+ const merged = { ...current, ...updatedUser };
30
+ localStorage.setItem('user', JSON.stringify(merged));
31
+ this.authService.setCurrentUser(merged);
32
+ }
33
+ })
34
+ );
35
+ }
36
+
37
+ uploadAvatar(file: File): Observable<{ url: string }> {
38
+ const formData = new FormData();
39
+ formData.append('file', file);
40
+ return this.http.post<{ url: string }>(\`\${this.apiUrl}/avatar\`, formData);
41
+ }
42
+ }
43
+ `;
44
+ }
@@ -0,0 +1 @@
1
+ export declare function devkitThemeServiceTemplate(): string;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devkitThemeServiceTemplate = devkitThemeServiceTemplate;
4
+ function devkitThemeServiceTemplate() {
5
+ return `import { Injectable } from '@angular/core';
6
+ import { BehaviorSubject } from 'rxjs';
7
+
8
+ const THEME_KEY = 'devkit-theme';
9
+
10
+ @Injectable({
11
+ providedIn: 'root'
12
+ })
13
+ export class ThemeService {
14
+ private isDarkSubject = new BehaviorSubject<boolean>(false);
15
+ isDark$ = this.isDarkSubject.asObservable();
16
+
17
+ get isDark(): boolean {
18
+ return this.isDarkSubject.getValue();
19
+ }
20
+
21
+ constructor() {
22
+ this.loadTheme();
23
+ }
24
+
25
+ toggle(): void {
26
+ this.setDark(!this.isDark);
27
+ }
28
+
29
+ setDark(dark: boolean): void {
30
+ this.isDarkSubject.next(dark);
31
+ localStorage.setItem(THEME_KEY, dark ? 'dark' : 'light');
32
+ if (dark) {
33
+ document.documentElement.classList.add('dark');
34
+ } else {
35
+ document.documentElement.classList.remove('dark');
36
+ }
37
+ }
38
+
39
+ private loadTheme(): void {
40
+ const saved = localStorage.getItem(THEME_KEY);
41
+ const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
42
+ const dark = saved ? saved === 'dark' : prefersDark;
43
+ this.setDark(dark);
44
+ }
45
+ }
46
+ `;
47
+ }
@@ -0,0 +1,3 @@
1
+ export declare function devkitHlmBadgeDirectiveTemplate(): {
2
+ ts: string;
3
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devkitHlmBadgeDirectiveTemplate = devkitHlmBadgeDirectiveTemplate;
4
+ function devkitHlmBadgeDirectiveTemplate() {
5
+ return {
6
+ ts: `import { Directive, input } from '@angular/core';
7
+ import { classes } from '@spartan-ng/helm/utils';
8
+ import { type VariantProps, cva } from 'class-variance-authority';
9
+
10
+ const badgeVariants = cva(
11
+ 'h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&>ng-icon]:text-[length:--spacing(3)] group/badge focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap focus-visible:ring-[3px] [&>ng-icon]:pointer-events-none',
12
+ {
13
+ variants: {
14
+ variant: {
15
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
16
+ secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
17
+ destructive:
18
+ 'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
19
+ outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
20
+ ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
21
+ link: 'text-primary underline-offset-4 hover:underline',
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: 'default',
26
+ },
27
+ },
28
+ );
29
+
30
+ export type BadgeVariants = VariantProps<typeof badgeVariants>;
31
+
32
+ @Directive({
33
+ selector: '[hlmBadge],hlm-badge',
34
+ host: {
35
+ 'data-slot': 'badge',
36
+ '[attr.data-variant]': 'variant()',
37
+ },
38
+ standalone: true,
39
+ })
40
+ export class HlmBadge {
41
+ public readonly variant = input<BadgeVariants['variant']>('default');
42
+
43
+ constructor() {
44
+ classes(() => badgeVariants({ variant: this.variant() }));
45
+ }
46
+ }
47
+ `
48
+ };
49
+ }
50
+ // 19. Template do devkit-badge (adaptado para importar HlmBadge oficial)
@@ -0,0 +1,3 @@
1
+ export declare function devkitHlmButtonDirectiveTemplate(): {
2
+ ts: string;
3
+ };
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devkitHlmButtonDirectiveTemplate = devkitHlmButtonDirectiveTemplate;
4
+ function devkitHlmButtonDirectiveTemplate() {
5
+ return {
6
+ ts: `import { Directive, input, signal } from '@angular/core';
7
+ import { BrnButton } from '@spartan-ng/brain/button';
8
+ import { classes } from '@spartan-ng/helm/utils';
9
+ import { cva, type VariantProps } from 'class-variance-authority';
10
+ import type { ClassValue } from 'clsx';
11
+ import { injectBrnButtonConfig } from './hlm-button.token';
12
+
13
+ export const buttonVariants = cva(
14
+ "focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px data-[matches-spartan-invalid=true]:ring-3 [&_ng-icon:not([class*='text-'])]:text-[length:--spacing(4)] group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_ng-icon]:pointer-events-none [&_ng-icon]:shrink-0",
15
+ {
16
+ variants: {
17
+ variant: {
18
+ default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
19
+ outline:
20
+ 'border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground',
21
+ secondary:
22
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
23
+ ghost:
24
+ 'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
25
+ destructive:
26
+ 'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
27
+ link: 'text-primary underline-offset-4 hover:underline',
28
+ },
29
+ size: {
30
+ default:
31
+ 'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pe-2 has-data-[icon=inline-start]:ps-2',
32
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_ng-icon:not([class*='text-'])]:text-[length:--spacing(3)]",
33
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pe-1.5 has-data-[icon=inline-start]:ps-1.5 [&_ng-icon:not([class*='text-'])]:text-[length:--spacing(3.5)]",
34
+ lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pe-3 has-data-[icon=inline-start]:ps-3',
35
+ icon: 'size-8',
36
+ 'icon-xs':
37
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_ng-icon:not([class*='text-'])]:text-[length:--spacing(3)]",
38
+ 'icon-sm':
39
+ 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
40
+ 'icon-lg': 'size-9',
41
+ },
42
+ },
43
+ defaultVariants: {
44
+ variant: 'default',
45
+ size: 'default',
46
+ },
47
+ },
48
+ );
49
+
50
+ export type ButtonVariants = VariantProps<typeof buttonVariants>;
51
+
52
+ @Directive({
53
+ selector: 'button[hlmBtn], a[hlmBtn]',
54
+ exportAs: 'hlmBtn',
55
+ hostDirectives: [{ directive: BrnButton, inputs: ['disabled'] }],
56
+ host: { 'data-slot': 'button' },
57
+ standalone: true,
58
+ })
59
+ export class HlmButton {
60
+ private readonly _config = injectBrnButtonConfig();
61
+
62
+ private readonly _additionalClasses = signal<ClassValue>('');
63
+
64
+ public readonly variant = input<ButtonVariants['variant']>(this._config.variant);
65
+
66
+ public readonly size = input<ButtonVariants['size']>(this._config.size);
67
+
68
+ constructor() {
69
+ classes(() => [
70
+ buttonVariants({ variant: this.variant(), size: this.size() }),
71
+ this._additionalClasses(),
72
+ ]);
73
+ }
74
+
75
+ setClass(classes: string): void {
76
+ this._additionalClasses.set(classes);
77
+ }
78
+ }
79
+ `
80
+ };
81
+ }
82
+ // Template do hlm-button.token.ts oficial
@@ -0,0 +1,3 @@
1
+ export declare function devkitHlmButtonTokenTemplate(): {
2
+ ts: string;
3
+ };
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.devkitHlmButtonTokenTemplate = devkitHlmButtonTokenTemplate;
4
+ function devkitHlmButtonTokenTemplate() {
5
+ return {
6
+ ts: `import { InjectionToken, type ValueProvider, inject } from '@angular/core';
7
+ import type { ButtonVariants } from './hlm-button';
8
+
9
+ export interface BrnButtonConfig {
10
+ variant: ButtonVariants['variant'];
11
+ size: ButtonVariants['size'];
12
+ }
13
+
14
+ const defaultConfig: BrnButtonConfig = {
15
+ variant: 'default',
16
+ size: 'default',
17
+ };
18
+
19
+ const BrnButtonConfigToken = new InjectionToken<BrnButtonConfig>('BrnButtonConfig');
20
+
21
+ export function provideBrnButtonConfig(config: Partial<BrnButtonConfig>): ValueProvider {
22
+ return { provide: BrnButtonConfigToken, useValue: { ...defaultConfig, ...config } };
23
+ }
24
+
25
+ export function injectBrnButtonConfig(): BrnButtonConfig {
26
+ return inject(BrnButtonConfigToken, { optional: true }) ?? defaultConfig;
27
+ }
28
+ `
29
+ };
30
+ }
31
+ // 17. Template da diretiva hlmInput do Spartan local (hlm-input.ts oficial)
@@ -0,0 +1,3 @@
1
+ export declare function devkitHlmCoreTemplate(): {
2
+ ts: string;
3
+ };