@hed-hog/core 0.0.151 → 0.0.153

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 (108) hide show
  1. package/dist/dashboard/dashboard/dashboard.controller.d.ts +214 -9
  2. package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
  3. package/dist/dashboard/dashboard/dashboard.controller.js +35 -35
  4. package/dist/dashboard/dashboard/dashboard.controller.js.map +1 -1
  5. package/dist/dashboard/dashboard/dashboard.module.d.ts +1 -1
  6. package/dist/dashboard/dashboard/dashboard.module.d.ts.map +1 -1
  7. package/dist/dashboard/dashboard/dashboard.module.js +11 -6
  8. package/dist/dashboard/dashboard/dashboard.module.js.map +1 -1
  9. package/dist/dashboard/dashboard/dashboard.service.d.ts +217 -16
  10. package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
  11. package/dist/dashboard/dashboard/dashboard.service.js +115 -28
  12. package/dist/dashboard/dashboard/dashboard.service.js.map +1 -1
  13. package/dist/dashboard/dashboard/dto/create.dto.d.ts +4 -2
  14. package/dist/dashboard/dashboard/dto/create.dto.d.ts.map +1 -1
  15. package/dist/dashboard/dashboard/dto/create.dto.js +10 -1
  16. package/dist/dashboard/dashboard/dto/create.dto.js.map +1 -1
  17. package/dist/dashboard/dashboard/dto/index.d.ts +5 -0
  18. package/dist/dashboard/dashboard/dto/index.d.ts.map +1 -0
  19. package/dist/dashboard/dashboard/dto/index.js +25 -0
  20. package/dist/dashboard/dashboard/dto/index.js.map +1 -0
  21. package/dist/dashboard/dashboard/dto/update.dto.d.ts +5 -4
  22. package/dist/dashboard/dashboard/dto/update.dto.d.ts.map +1 -1
  23. package/dist/dashboard/dashboard/dto/update.dto.js +27 -3
  24. package/dist/dashboard/dashboard/dto/update.dto.js.map +1 -1
  25. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +114 -9
  26. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
  27. package/dist/dashboard/dashboard-component/dashboard-component.controller.js +35 -36
  28. package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
  29. package/dist/dashboard/dashboard-component/dashboard-component.module.js +4 -3
  30. package/dist/dashboard/dashboard-component/dashboard-component.module.js.map +1 -1
  31. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +116 -15
  32. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
  33. package/dist/dashboard/dashboard-component/dashboard-component.service.js +109 -22
  34. package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
  35. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +4 -2
  36. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
  37. package/dist/dashboard/dashboard-component/dto/create.dto.js +10 -1
  38. package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
  39. package/dist/dashboard/dashboard-component/dto/index.d.ts +5 -0
  40. package/dist/dashboard/dashboard-component/dto/index.d.ts.map +1 -0
  41. package/dist/dashboard/dashboard-component/dto/index.js +25 -0
  42. package/dist/dashboard/dashboard-component/dto/index.js.map +1 -0
  43. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +13 -4
  44. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
  45. package/dist/dashboard/dashboard-component/dto/update.dto.js +67 -3
  46. package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
  47. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +56 -0
  48. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  49. package/dist/dashboard/dashboard-core/dashboard-core.controller.js +65 -0
  50. package/dist/dashboard/dashboard-core/dashboard-core.controller.js.map +1 -1
  51. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +52 -0
  52. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  53. package/dist/dashboard/dashboard-core/dashboard-core.service.js +221 -0
  54. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  55. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +26 -29
  56. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
  57. package/dist/dashboard/dashboard-item/dashboard-item.controller.js +24 -43
  58. package/dist/dashboard/dashboard-item/dashboard-item.controller.js.map +1 -1
  59. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +30 -34
  60. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  61. package/dist/dashboard/dashboard-item/dashboard-item.service.js +51 -35
  62. package/dist/dashboard/dashboard-item/dashboard-item.service.js.map +1 -1
  63. package/dist/dashboard/dashboard-item/dto/index.d.ts +5 -0
  64. package/dist/dashboard/dashboard-item/dto/index.d.ts.map +1 -0
  65. package/dist/dashboard/dashboard-item/dto/index.js +25 -0
  66. package/dist/dashboard/dashboard-item/dto/index.js.map +1 -0
  67. package/dist/dashboard/dashboard-item/dto/update.dto.d.ts +7 -4
  68. package/dist/dashboard/dashboard-item/dto/update.dto.d.ts.map +1 -1
  69. package/dist/dashboard/dashboard-item/dto/update.dto.js +42 -3
  70. package/dist/dashboard/dashboard-item/dto/update.dto.js.map +1 -1
  71. package/dist/dashboard/dashboard.module.js +1 -1
  72. package/dist/dashboard/dashboard.module.js.map +1 -1
  73. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  74. package/dist/oauth/oauth.controller.js.map +1 -1
  75. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  76. package/dist/oauth/providers/microsoft-entra-id.provider.js +7 -4
  77. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  78. package/dist/setting/setting.service.d.ts.map +1 -1
  79. package/dist/setting/setting.service.js +1 -0
  80. package/dist/setting/setting.service.js.map +1 -1
  81. package/hedhog/data/route.yaml +44 -14
  82. package/hedhog/data/setting_group.yaml +10 -0
  83. package/hedhog/query/dashboard-seed.sql +131 -0
  84. package/package.json +1 -1
  85. package/src/dashboard/dashboard/dashboard.controller.ts +26 -23
  86. package/src/dashboard/dashboard/dashboard.module.ts +7 -2
  87. package/src/dashboard/dashboard/dashboard.service.ts +125 -44
  88. package/src/dashboard/dashboard/dto/create.dto.ts +12 -3
  89. package/src/dashboard/dashboard/dto/index.ts +7 -0
  90. package/src/dashboard/dashboard/dto/update.dto.ts +17 -3
  91. package/src/dashboard/dashboard-component/dashboard-component.controller.ts +22 -19
  92. package/src/dashboard/dashboard-component/dashboard-component.module.ts +3 -3
  93. package/src/dashboard/dashboard-component/dashboard-component.service.ts +128 -39
  94. package/src/dashboard/dashboard-component/dto/create.dto.ts +12 -3
  95. package/src/dashboard/dashboard-component/dto/index.ts +7 -0
  96. package/src/dashboard/dashboard-component/dto/update.dto.ts +49 -3
  97. package/src/dashboard/dashboard-core/dashboard-core.controller.ts +53 -2
  98. package/src/dashboard/dashboard-core/dashboard-core.service.ts +271 -0
  99. package/src/dashboard/dashboard-item/dashboard-item.controller.ts +17 -26
  100. package/src/dashboard/dashboard-item/dashboard-item.service.ts +55 -47
  101. package/src/dashboard/dashboard-item/dto/index.ts +7 -0
  102. package/src/dashboard/dashboard-item/dto/update.dto.ts +27 -3
  103. package/src/dashboard/dashboard.module.ts +2 -2
  104. package/src/language/en.json +4 -1
  105. package/src/language/pt.json +4 -1
  106. package/src/oauth/oauth.controller.ts +1 -2
  107. package/src/oauth/providers/microsoft-entra-id.provider.ts +9 -4
  108. package/src/setting/setting.service.ts +1 -0
@@ -1,12 +1,12 @@
1
+ import { LocaleModule } from '@hed-hog/api-locale';
1
2
  import { PaginationModule } from '@hed-hog/api-pagination';
2
3
  import { PrismaModule } from '@hed-hog/api-prisma';
3
- import { LocaleModule } from '@hed-hog/api-locale';
4
4
  import { forwardRef, Module } from '@nestjs/common';
5
- import { DashboardComponentService } from './dashboard-component.service';
6
5
  import { DashboardComponentController } from './dashboard-component.controller';
6
+ import { DashboardComponentService } from './dashboard-component.service';
7
7
 
8
8
  @Module({
9
- imports: [forwardRef(() => LocaleModule), forwardRef(() => PrismaModule)],
9
+ imports: [forwardRef(() => LocaleModule), forwardRef(() => PaginationModule), forwardRef(() => PrismaModule)],
10
10
  controllers: [DashboardComponentController],
11
11
  providers: [DashboardComponentService],
12
12
  exports: [DashboardComponentService],
@@ -1,16 +1,16 @@
1
- import { PaginationDTO } from '@hed-hog/api-pagination';
1
+ import { getLocaleText } from '@hed-hog/api-locale';
2
+ import { PaginationService } from '@hed-hog/api-pagination';
2
3
  import { PrismaService } from '@hed-hog/api-prisma';
3
4
  import {
4
- BadRequestException,
5
+ forwardRef,
5
6
  Inject,
6
7
  Injectable,
7
- forwardRef,
8
+ NotFoundException
8
9
  } from '@nestjs/common';
9
- import { CreateDTO } from './dto/create.dto';
10
- import { DeleteDTO } from '@hed-hog/api';
11
- import { UpdateDTO } from './dto/update.dto';
12
- import { LocaleService } from '@hed-hog/api-locale';
13
-
10
+ import {
11
+ CreateDashboardComponentDTO,
12
+ UpdateDashboardComponentDTO,
13
+ } from './dto';
14
14
  @Injectable()
15
15
  export class DashboardComponentService {
16
16
  private readonly modelName = 'dashboard_component';
@@ -19,52 +19,141 @@ export class DashboardComponentService {
19
19
  constructor(
20
20
  @Inject(forwardRef(() => PrismaService))
21
21
  private readonly prismaService: PrismaService,
22
- @Inject(forwardRef(() => LocaleService))
23
- private readonly localeService: LocaleService,
22
+ @Inject(forwardRef(() => PaginationService))
23
+ private readonly paginationService: PaginationService,
24
24
  ) {}
25
25
 
26
- async list(locale: string, paginationParams: PaginationDTO) {
27
- return this.localeService.listModelWithLocale(
28
- locale,
29
- this.modelName,
26
+ async getAllComponents(paginationParams) {
27
+ return this.paginationService.paginate(
28
+ this.prismaService.dashboard_component,
30
29
  paginationParams,
30
+ {
31
+ include: {
32
+ dashboard_component_locale: {
33
+ include: {
34
+ locale: true,
35
+ },
36
+ },
37
+ },
38
+ orderBy: {
39
+ created_at: 'desc',
40
+ },
41
+ },
42
+ 'dashboardComponent',
31
43
  );
32
44
  }
33
45
 
34
- async get(id: number) {
35
- return this.localeService.getModelWithLocale(this.modelName, id);
46
+ async getComponent(id: number, locale: string) {
47
+ const component = await this.prismaService.dashboard_component.findUnique({
48
+ where: { id },
49
+ include: {
50
+ dashboard_component_locale: {
51
+ include: {
52
+ locale: true,
53
+ },
54
+ },
55
+ },
56
+ });
57
+
58
+ if (!component) {
59
+ throw new NotFoundException(
60
+ getLocaleText('dashboardComponentNotFound', locale, 'Dashboard component not found'),
61
+ );
62
+ }
63
+
64
+ return component;
36
65
  }
37
66
 
38
- async create(data: CreateDTO) {
39
- return this.localeService.createModelWithLocale(
40
- this.modelName,
41
- this.foreignKey,
42
- data,
43
- );
67
+ async createComponent(data: CreateDashboardComponentDTO, locale: string) {
68
+ const component = await this.prismaService.dashboard_component.create({
69
+ data: {
70
+ slug: data.slug,
71
+ path: data.path,
72
+ min_width: data.min_width,
73
+ max_width: data.max_width,
74
+ min_height: data.min_height,
75
+ max_height: data.max_height,
76
+ width: data.width,
77
+ height: data.height,
78
+ is_resizable: data.is_resizable ?? true,
79
+ },
80
+ });
81
+
82
+ if (data.locale) {
83
+ for (const [localeCode, localeData] of Object.entries(data.locale)) {
84
+ const localeRecord = await this.prismaService.locale.findFirst({
85
+ where: { code: localeCode },
86
+ });
87
+
88
+ if (localeRecord) {
89
+ await this.prismaService.dashboard_component_locale.create({
90
+ data: {
91
+ dashboard_component_id: component.id,
92
+ locale_id: localeRecord.id,
93
+ name: localeData.name,
94
+ },
95
+ });
96
+ }
97
+ }
98
+ }
99
+
100
+ return this.getComponent(component.id, locale);
44
101
  }
45
102
 
46
- async update({ id, data }: { id: number; data: UpdateDTO }) {
47
- return this.localeService.updateModelWithLocale(
48
- this.modelName,
49
- this.foreignKey,
50
- id,
51
- data,
52
- );
103
+ async updateComponent(id: number, data: UpdateDashboardComponentDTO, locale: string) {
104
+ await this.prismaService.dashboard_component.update({
105
+ where: { id },
106
+ data: {
107
+ slug: data.slug,
108
+ path: data.path,
109
+ min_width: data.min_width,
110
+ max_width: data.max_width,
111
+ min_height: data.min_height,
112
+ max_height: data.max_height,
113
+ width: data.width,
114
+ height: data.height,
115
+ is_resizable: data.is_resizable,
116
+ },
117
+ });
118
+
119
+ if (data.locale) {
120
+ await this.prismaService.dashboard_component_locale.deleteMany({
121
+ where: { dashboard_component_id: id },
122
+ });
123
+
124
+ for (const [localeCode, localeData] of Object.entries(data.locale)) {
125
+ const localeRecord = await this.prismaService.locale.findFirst({
126
+ where: { code: localeCode },
127
+ });
128
+
129
+ if (localeRecord) {
130
+ await this.prismaService.dashboard_component_locale.create({
131
+ data: {
132
+ dashboard_component_id: id,
133
+ locale_id: localeRecord.id,
134
+ name: localeData.name,
135
+ },
136
+ });
137
+ }
138
+ }
139
+ }
140
+
141
+ return this.getComponent(id, locale);
53
142
  }
54
143
 
55
- async delete({ ids }: DeleteDTO): Promise<{count:number}> {
56
- if (ids == undefined || ids == null) {
57
- throw new BadRequestException(
58
- 'You must select at least one item to delete.',
144
+ async deleteComponent(id: number, locale: string) {
145
+ const component = await this.prismaService.dashboard_component.findUnique({
146
+ where: { id },
147
+ });
148
+
149
+ if (!component) {
150
+ throw new NotFoundException(
151
+ getLocaleText('dashboardComponentNotFound', locale, 'Dashboard component not found'),
59
152
  );
60
153
  }
61
154
 
62
- return this.prismaService.dashboard_component.deleteMany({
63
- where: {
64
- id: {
65
- in: ids,
66
- },
67
- },
155
+ return this.prismaService.dashboard_component.delete({
156
+ where: { id },
68
157
  });
69
158
  }
70
159
  }
@@ -1,7 +1,7 @@
1
- import { getLocaleText, WithLocaleDTO } from '@hed-hog/api-locale';
2
- import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator';
1
+ import { getLocaleText } from '@hed-hog/api-locale';
2
+ import { IsBoolean, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from 'class-validator';
3
3
 
4
- export class CreateDTO extends WithLocaleDTO {
4
+ export class CreateDTO {
5
5
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
6
6
  slug: string;
7
7
 
@@ -33,4 +33,13 @@ export class CreateDTO extends WithLocaleDTO {
33
33
  @IsOptional()
34
34
  @IsBoolean({ message: (args) => getLocaleText('validation.booleanRequired', args.value) })
35
35
  is_resizable?: boolean;
36
+
37
+ @IsObject({
38
+ message: (args) =>
39
+ getLocaleText('validation.localeMustBeObject', args.value),
40
+ })
41
+ @IsNotEmpty({
42
+ message: (args) => getLocaleText('validation.localeRequired', args.value),
43
+ })
44
+ locale: Record<string, { name: string }>;
36
45
  }
@@ -0,0 +1,7 @@
1
+ export * from './create.dto';
2
+ export * from './update.dto';
3
+
4
+ // Aliases para compatibilidade
5
+ export { CreateDTO as CreateDashboardComponentDTO } from './create.dto';
6
+ export { UpdateDTO as UpdateDashboardComponentDTO } from './update.dto';
7
+
@@ -1,4 +1,50 @@
1
- import { PartialType } from '@nestjs/mapped-types';
2
- import { CreateDTO } from './create.dto';
1
+ import { getLocaleText } from '@hed-hog/api-locale';
2
+ import { IsBoolean, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from 'class-validator';
3
3
 
4
- export class UpdateDTO extends PartialType(CreateDTO) {}
4
+ export class UpdateDTO {
5
+ @IsOptional()
6
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
7
+ slug?: string;
8
+
9
+ @IsOptional()
10
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
11
+ path?: string;
12
+
13
+ @IsOptional()
14
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
15
+ min_width?: number;
16
+
17
+ @IsOptional()
18
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
19
+ max_width?: number;
20
+
21
+ @IsOptional()
22
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
23
+ min_height?: number;
24
+
25
+ @IsOptional()
26
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
27
+ max_height?: number;
28
+
29
+ @IsOptional()
30
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
31
+ width?: number;
32
+
33
+ @IsOptional()
34
+ @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
35
+ height?: number;
36
+
37
+ @IsOptional()
38
+ @IsBoolean({ message: (args) => getLocaleText('validation.booleanRequired', args.value) })
39
+ is_resizable?: boolean;
40
+
41
+ @IsOptional()
42
+ @IsObject({
43
+ message: (args) =>
44
+ getLocaleText('validation.localeMustBeObject', args.value),
45
+ })
46
+ @IsNotEmpty({
47
+ message: (args) => getLocaleText('validation.localeRequired', args.value),
48
+ })
49
+ locale?: Record<string, { name: string }>;
50
+ }
@@ -1,7 +1,8 @@
1
- import { Role } from '@hed-hog/api';
1
+ import { Role, User } from '@hed-hog/api';
2
2
  import { Locale } from '@hed-hog/api-locale';
3
- import { Controller, Get, Param } from '@nestjs/common';
3
+ import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
4
4
  import { DashboardCoreService } from './dashboard-core.service';
5
+
5
6
  @Role()
6
7
  @Controller('dashboard-core')
7
8
  export class DashboardCoreController {
@@ -11,4 +12,54 @@ export class DashboardCoreController {
11
12
  fromSlug(@Param('slug') slug: string, @Locale() locale) {
12
13
  return this.dashboardCoreService.fromSlug(slug, locale);
13
14
  }
15
+
16
+ @Get('stats/overview')
17
+ getStatistics() {
18
+ return this.dashboardCoreService.getStatistics();
19
+ }
20
+
21
+ @Get('stats/user-growth')
22
+ getUserGrowth() {
23
+ return this.dashboardCoreService.getUserGrowth();
24
+ }
25
+
26
+ @Get('layout/:slug')
27
+ getUserLayout(@User() user, @Param('slug') slug: string) {
28
+ return this.dashboardCoreService.getUserLayout(user.id, slug);
29
+ }
30
+
31
+ @Post('layout/:slug')
32
+ saveUserLayout(
33
+ @User() user,
34
+ @Param('slug') slug: string,
35
+ @Body() body: { layout: Array<{ i: string; x: number; y: number; w: number; h: number }> },
36
+ ) {
37
+ return this.dashboardCoreService.saveUserLayout(user.id, slug, body.layout);
38
+ }
39
+
40
+ @Post('widget/:slug')
41
+ addWidget(
42
+ @User() user,
43
+ @Param('slug') slug: string,
44
+ @Body() body: { componentSlug: string },
45
+ ) {
46
+ return this.dashboardCoreService.addWidgetToUserDashboard(
47
+ user.id,
48
+ slug,
49
+ body.componentSlug,
50
+ );
51
+ }
52
+
53
+ @Delete('widget/:slug/:widgetId')
54
+ removeWidget(
55
+ @User() user,
56
+ @Param('slug') slug: string,
57
+ @Param('widgetId') widgetId: string,
58
+ ) {
59
+ return this.dashboardCoreService.removeWidgetFromUserDashboard(
60
+ user.id,
61
+ slug,
62
+ widgetId,
63
+ );
64
+ }
14
65
  }
@@ -24,4 +24,275 @@ export class DashboardCoreService {
24
24
  },
25
25
  });
26
26
  }
27
+
28
+ async getStatistics() {
29
+ const now = new Date();
30
+ const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
31
+ const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
32
+
33
+ // Total de usuários
34
+ const totalUsers = await this.prismaService.user.count();
35
+
36
+ // Usuários criados nos últimos 7 dias
37
+ const recentUsers = await this.prismaService.user.count({
38
+ where: {
39
+ created_at: {
40
+ gte: sevenDaysAgo,
41
+ },
42
+ },
43
+ });
44
+
45
+ // Usuários ativos (criados nos últimos 30 dias como aproximação)
46
+ const activeUsers = await this.prismaService.user.count({
47
+ where: {
48
+ created_at: {
49
+ gte: thirtyDaysAgo,
50
+ },
51
+ },
52
+ });
53
+
54
+ // Total de roles
55
+ const totalRoles = await this.prismaService.role.count();
56
+
57
+ // Total de menus
58
+ const totalMenus = await this.prismaService.menu.count();
59
+
60
+ // Total de rotas
61
+ const totalRoutes = await this.prismaService.route.count();
62
+
63
+ return {
64
+ totalUsers,
65
+ recentUsers,
66
+ activeUsers,
67
+ totalRoles,
68
+ totalMenus,
69
+ totalRoutes,
70
+ };
71
+ }
72
+
73
+ async getUserGrowth() {
74
+ const now = new Date();
75
+ const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
76
+
77
+ // Contagem de usuários criados por dia nos últimos 30 dias
78
+ const usersByDay = await this.prismaService.$queryRaw<
79
+ Array<{ date: Date; count: bigint }>
80
+ >`
81
+ SELECT DATE("created_at") as date, COUNT(*) as count
82
+ FROM "user"
83
+ WHERE "created_at" >= ${thirtyDaysAgo}
84
+ GROUP BY DATE("created_at")
85
+ ORDER BY date ASC
86
+ `;
87
+
88
+ return usersByDay.map((item) => ({
89
+ date: item.date,
90
+ count: Number(item.count),
91
+ }));
92
+ }
93
+
94
+ async getUserLayout(userId: number, slug: string) {
95
+ // Busca os itens do dashboard para este slug
96
+ const dashboardItems = await this.prismaService.dashboard_item.findMany({
97
+ where: {
98
+ dashboard: {
99
+ slug,
100
+ },
101
+ },
102
+ include: {
103
+ dashboard_user: {
104
+ where: {
105
+ user_id: userId,
106
+ },
107
+ },
108
+ dashboard_component: true,
109
+ },
110
+ });
111
+
112
+ return dashboardItems.map((item) => {
113
+ const userLayout = item.dashboard_user[0];
114
+ return {
115
+ i: `widget-${item.id}`,
116
+ component_id: item.component_id,
117
+ slug: item.dashboard_component.slug,
118
+ x: userLayout?.x_axis ?? item.x_axis ?? 0,
119
+ y: userLayout?.y_axis ?? item.y_axis ?? 0,
120
+ w: userLayout?.width ?? item.width ?? 2,
121
+ h: userLayout?.height ?? item.height ?? 2,
122
+ minW: item.dashboard_component.min_width,
123
+ maxW: item.dashboard_component.max_width,
124
+ minH: item.dashboard_component.min_height,
125
+ maxH: item.dashboard_component.max_height,
126
+ isResizable: item.dashboard_component.is_resizable ?? true,
127
+ };
128
+ });
129
+ }
130
+
131
+ async saveUserLayout(
132
+ userId: number,
133
+ slug: string,
134
+ layout: Array<{
135
+ i: string;
136
+ x: number;
137
+ y: number;
138
+ w: number;
139
+ h: number;
140
+ }>,
141
+ ) {
142
+ // Busca o dashboard
143
+ const dashboard = await this.prismaService.dashboard.findFirst({
144
+ where: { slug },
145
+ });
146
+
147
+ if (!dashboard) {
148
+ throw new Error(`Dashboard with slug '${slug}' not found`);
149
+ }
150
+
151
+ // Para cada item do layout, salva ou atualiza o dashboard_user
152
+ for (const item of layout) {
153
+ // Extrai o ID do item do identificador
154
+ const itemId = parseInt(item.i.replace('widget-', ''));
155
+
156
+ // Verifica se o item existe
157
+ const dashboardItem = await this.prismaService.dashboard_item.findFirst({
158
+ where: {
159
+ id: itemId,
160
+ dashboard_id: dashboard.id,
161
+ },
162
+ });
163
+
164
+ if (!dashboardItem) {
165
+ continue;
166
+ }
167
+
168
+ // Busca se já existe um dashboard_user para este item e usuário
169
+ const existingUserLayout =
170
+ await this.prismaService.dashboard_user.findFirst({
171
+ where: {
172
+ item_id: itemId,
173
+ user_id: userId,
174
+ },
175
+ });
176
+
177
+ if (existingUserLayout) {
178
+ // Atualiza o layout existente
179
+ await this.prismaService.dashboard_user.update({
180
+ where: {
181
+ id: existingUserLayout.id,
182
+ },
183
+ data: {
184
+ x_axis: item.x,
185
+ y_axis: item.y,
186
+ width: item.w,
187
+ height: item.h,
188
+ },
189
+ });
190
+ } else {
191
+ // Cria um novo layout de usuário
192
+ await this.prismaService.dashboard_user.create({
193
+ data: {
194
+ item_id: itemId,
195
+ user_id: userId,
196
+ x_axis: item.x,
197
+ y_axis: item.y,
198
+ width: item.w,
199
+ height: item.h,
200
+ },
201
+ });
202
+ }
203
+ }
204
+
205
+ return { success: true };
206
+ }
207
+
208
+ async addWidgetToUserDashboard(
209
+ userId: number,
210
+ slug: string,
211
+ componentSlug: string,
212
+ ) {
213
+ // Busca o dashboard
214
+ const dashboard = await this.prismaService.dashboard.findFirst({
215
+ where: { slug },
216
+ });
217
+
218
+ if (!dashboard) {
219
+ throw new Error(`Dashboard with slug '${slug}' not found`);
220
+ }
221
+
222
+ // Busca o componente
223
+ const component = await this.prismaService.dashboard_component.findFirst({
224
+ where: { slug: componentSlug },
225
+ });
226
+
227
+ if (!component) {
228
+ throw new Error(`Component with slug '${componentSlug}' not found`);
229
+ }
230
+
231
+ // Verifica se já existe um item para este componente no dashboard
232
+ let dashboardItem = await this.prismaService.dashboard_item.findFirst({
233
+ where: {
234
+ dashboard_id: dashboard.id,
235
+ component_id: component.id,
236
+ },
237
+ });
238
+
239
+ // Se não existe, cria um novo item
240
+ if (!dashboardItem) {
241
+ dashboardItem = await this.prismaService.dashboard_item.create({
242
+ data: {
243
+ dashboard_id: dashboard.id,
244
+ component_id: component.id,
245
+ width: component.width,
246
+ height: component.height,
247
+ x_axis: 0,
248
+ y_axis: 0,
249
+ },
250
+ });
251
+ }
252
+
253
+ // Cria um registro de dashboard_user para este usuário
254
+ const userLayout = await this.prismaService.dashboard_user.create({
255
+ data: {
256
+ item_id: dashboardItem.id,
257
+ user_id: userId,
258
+ width: component.width,
259
+ height: component.height,
260
+ x_axis: 0,
261
+ y_axis: 999, // Coloca no final
262
+ },
263
+ });
264
+
265
+ return {
266
+ i: `widget-${dashboardItem.id}`,
267
+ component_id: component.id,
268
+ slug: component.slug,
269
+ x: userLayout.x_axis,
270
+ y: userLayout.y_axis,
271
+ w: userLayout.width,
272
+ h: userLayout.height,
273
+ minW: component.min_width,
274
+ maxW: component.max_width,
275
+ minH: component.min_height,
276
+ maxH: component.max_height,
277
+ isResizable: component.is_resizable ?? true,
278
+ };
279
+ }
280
+
281
+ async removeWidgetFromUserDashboard(
282
+ userId: number,
283
+ slug: string,
284
+ widgetId: string,
285
+ ) {
286
+ const itemId = parseInt(widgetId.replace('widget-', ''));
287
+
288
+ // Remove o dashboard_user
289
+ await this.prismaService.dashboard_user.deleteMany({
290
+ where: {
291
+ item_id: itemId,
292
+ user_id: userId,
293
+ },
294
+ });
295
+
296
+ return { success: true };
297
+ }
27
298
  }