@hed-hog/core 0.0.298 → 0.0.299

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 (139) hide show
  1. package/dist/dashboard/dashboard/dashboard.controller.d.ts +3 -0
  2. package/dist/dashboard/dashboard/dashboard.controller.d.ts.map +1 -1
  3. package/dist/dashboard/dashboard/dashboard.service.d.ts +3 -0
  4. package/dist/dashboard/dashboard/dashboard.service.d.ts.map +1 -1
  5. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts +12 -0
  6. package/dist/dashboard/dashboard-component/dashboard-component.controller.d.ts.map +1 -1
  7. package/dist/dashboard/dashboard-component/dashboard-component.controller.js +22 -0
  8. package/dist/dashboard/dashboard-component/dashboard-component.controller.js.map +1 -1
  9. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts +15 -0
  10. package/dist/dashboard/dashboard-component/dashboard-component.service.d.ts.map +1 -1
  11. package/dist/dashboard/dashboard-component/dashboard-component.service.js +110 -3
  12. package/dist/dashboard/dashboard-component/dashboard-component.service.js.map +1 -1
  13. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts +1 -0
  14. package/dist/dashboard/dashboard-component/dto/create.dto.d.ts.map +1 -1
  15. package/dist/dashboard/dashboard-component/dto/create.dto.js +5 -0
  16. package/dist/dashboard/dashboard-component/dto/create.dto.js.map +1 -1
  17. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts +1 -0
  18. package/dist/dashboard/dashboard-component/dto/update.dto.d.ts.map +1 -1
  19. package/dist/dashboard/dashboard-component/dto/update.dto.js +5 -0
  20. package/dist/dashboard/dashboard-component/dto/update.dto.js.map +1 -1
  21. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts +1 -0
  22. package/dist/dashboard/dashboard-component-role/dashboard-component-role.controller.d.ts.map +1 -1
  23. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts +1 -0
  24. package/dist/dashboard/dashboard-component-role/dashboard-component-role.service.d.ts.map +1 -1
  25. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +7 -1
  26. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts.map +1 -1
  27. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +7 -1
  28. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts.map +1 -1
  29. package/dist/dashboard/dashboard-core/dashboard-core.service.js +88 -4
  30. package/dist/dashboard/dashboard-core/dashboard-core.service.js.map +1 -1
  31. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts +1 -0
  32. package/dist/dashboard/dashboard-item/dashboard-item.controller.d.ts.map +1 -1
  33. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts +1 -0
  34. package/dist/dashboard/dashboard-item/dashboard-item.service.d.ts.map +1 -1
  35. package/hedhog/data/dashboard_item.yaml +1 -1
  36. package/hedhog/data/route.yaml +12 -0
  37. package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +141 -24
  38. package/hedhog/frontend/app/dashboard/[slug]/types.ts.ejs +3 -0
  39. package/hedhog/frontend/app/dashboard/[slug]/widget-renderer.tsx.ejs +136 -23
  40. package/hedhog/frontend/app/dashboard/components/add-widget-selector-dialog.tsx.ejs +266 -85
  41. package/hedhog/frontend/app/dashboard/components/widgets/core..gitkeep.ejs +11 -0
  42. package/hedhog/frontend/app/dashboard/components/widgets/core.account-security.tsx.ejs +192 -0
  43. package/hedhog/frontend/app/dashboard/components/widgets/core.user-sessions.tsx.ejs +236 -0
  44. package/hedhog/frontend/app/dashboard/components/widgets/finance.alerts.tsx.ejs +108 -0
  45. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-balance-kpi.tsx.ejs +66 -0
  46. package/hedhog/frontend/app/dashboard/components/widgets/finance.cash-flow-chart.tsx.ejs +122 -0
  47. package/hedhog/frontend/app/dashboard/components/widgets/finance.default-kpi.tsx.ejs +63 -0
  48. package/hedhog/frontend/app/dashboard/components/widgets/finance.payable-30d-kpi.tsx.ejs +73 -0
  49. package/hedhog/frontend/app/dashboard/components/widgets/finance.receivable-30d-kpi.tsx.ejs +73 -0
  50. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-payable.tsx.ejs +123 -0
  51. package/hedhog/frontend/app/dashboard/components/widgets/finance.upcoming-receivable.tsx.ejs +118 -0
  52. package/hedhog/frontend/messages/en.json +3 -0
  53. package/hedhog/frontend/messages/pt.json +3 -0
  54. package/hedhog/frontend/public/dashboard-previews/.gitkeep +12 -0
  55. package/hedhog/frontend/public/dashboard-previews/account-security.png +0 -0
  56. package/hedhog/frontend/public/dashboard-previews/active-users-card.png +0 -0
  57. package/hedhog/frontend/public/dashboard-previews/activity-timeline.png +0 -0
  58. package/hedhog/frontend/public/dashboard-previews/cash-balance-kpi.png +0 -0
  59. package/hedhog/frontend/public/dashboard-previews/cash-flow-chart.png +0 -0
  60. package/hedhog/frontend/public/dashboard-previews/default-kpi.png +0 -0
  61. package/hedhog/frontend/public/dashboard-previews/email-notifications.png +0 -0
  62. package/hedhog/frontend/public/dashboard-previews/financial-alerts.png +0 -0
  63. package/hedhog/frontend/public/dashboard-previews/login-history-chart.png +0 -0
  64. package/hedhog/frontend/public/dashboard-previews/mail-sent-card.png +0 -0
  65. package/hedhog/frontend/public/dashboard-previews/mail-sent-chart.png +0 -0
  66. package/hedhog/frontend/public/dashboard-previews/menus-card.png +0 -0
  67. package/hedhog/frontend/public/dashboard-previews/payable-30d-kpi.png +0 -0
  68. package/hedhog/frontend/public/dashboard-previews/permissions-card.png +0 -0
  69. package/hedhog/frontend/public/dashboard-previews/permissions-chart.png +0 -0
  70. package/hedhog/frontend/public/dashboard-previews/profile-card.png +0 -0
  71. package/hedhog/frontend/public/dashboard-previews/receivable-30d-kpi.png +0 -0
  72. package/hedhog/frontend/public/dashboard-previews/routes-card.png +0 -0
  73. package/hedhog/frontend/public/dashboard-previews/session-activity-chart.png +0 -0
  74. package/hedhog/frontend/public/dashboard-previews/sessions-today-card.png +0 -0
  75. package/hedhog/frontend/public/dashboard-previews/stat-access-level.png +0 -0
  76. package/hedhog/frontend/public/dashboard-previews/stat-actions-today.png +0 -0
  77. package/hedhog/frontend/public/dashboard-previews/stat-consecutive-days.png +0 -0
  78. package/hedhog/frontend/public/dashboard-previews/stat-online-time.png +0 -0
  79. package/hedhog/frontend/public/dashboard-previews/upcoming-payable.png +0 -0
  80. package/hedhog/frontend/public/dashboard-previews/upcoming-receivable.png +0 -0
  81. package/hedhog/frontend/public/dashboard-previews/user-growth-chart.png +0 -0
  82. package/hedhog/frontend/public/dashboard-previews/user-roles.png +0 -0
  83. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/account-security.tsx.ejs +33 -29
  84. package/hedhog/frontend/widgets/active-users-card.tsx.ejs +58 -0
  85. package/hedhog/frontend/widgets/activity-timeline.tsx.ejs +223 -0
  86. package/hedhog/frontend/widgets/email-notifications.tsx.ejs +226 -0
  87. package/hedhog/frontend/widgets/locale-config.tsx.ejs +168 -0
  88. package/hedhog/frontend/widgets/login-history-chart.tsx.ejs +115 -0
  89. package/hedhog/frontend/widgets/mail-config.tsx.ejs +199 -0
  90. package/hedhog/frontend/widgets/mail-sent-card.tsx.ejs +58 -0
  91. package/hedhog/frontend/widgets/mail-sent-chart.tsx.ejs +149 -0
  92. package/hedhog/frontend/widgets/menus-card.tsx.ejs +58 -0
  93. package/hedhog/frontend/widgets/oauth-config.tsx.ejs +175 -0
  94. package/hedhog/frontend/widgets/permissions-card.tsx.ejs +61 -0
  95. package/hedhog/frontend/widgets/permissions-chart.tsx.ejs +156 -0
  96. package/hedhog/frontend/widgets/profile-card.tsx.ejs +186 -0
  97. package/hedhog/frontend/widgets/routes-card.tsx.ejs +58 -0
  98. package/hedhog/frontend/widgets/session-activity-chart.tsx.ejs +183 -0
  99. package/hedhog/frontend/widgets/sessions-today-card.tsx.ejs +62 -0
  100. package/hedhog/frontend/widgets/stat-access-level.tsx.ejs +57 -0
  101. package/hedhog/frontend/widgets/stat-actions-today.tsx.ejs +57 -0
  102. package/hedhog/frontend/widgets/stat-consecutive-days.tsx.ejs +57 -0
  103. package/hedhog/frontend/widgets/stat-online-time.tsx.ejs +57 -0
  104. package/hedhog/frontend/widgets/storage-config.tsx.ejs +196 -0
  105. package/hedhog/frontend/widgets/theme-config.tsx.ejs +213 -0
  106. package/hedhog/frontend/widgets/user-growth-chart.tsx.ejs +210 -0
  107. package/hedhog/frontend/widgets/user-roles.tsx.ejs +132 -0
  108. package/hedhog/frontend/{app/dashboard/components/widgets → widgets}/user-sessions.tsx.ejs +1 -1
  109. package/hedhog/table/dashboard_component.yaml +7 -0
  110. package/package.json +4 -4
  111. package/src/dashboard/dashboard-component/dashboard-component.controller.ts +36 -12
  112. package/src/dashboard/dashboard-component/dashboard-component.service.ts +150 -3
  113. package/src/dashboard/dashboard-component/dto/create.dto.ts +4 -0
  114. package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -0
  115. package/src/dashboard/dashboard-core/dashboard-core.service.ts +108 -5
  116. /package/hedhog/frontend/app/dashboard/components/widgets/{active-users-card.tsx.ejs → core.active-users-card.tsx.ejs} +0 -0
  117. /package/hedhog/frontend/app/dashboard/components/widgets/{activity-timeline.tsx.ejs → core.activity-timeline.tsx.ejs} +0 -0
  118. /package/hedhog/frontend/app/dashboard/components/widgets/{email-notifications.tsx.ejs → core.email-notifications.tsx.ejs} +0 -0
  119. /package/hedhog/frontend/app/dashboard/components/widgets/{locale-config.tsx.ejs → core.locale-config.tsx.ejs} +0 -0
  120. /package/hedhog/frontend/app/dashboard/components/widgets/{login-history-chart.tsx.ejs → core.login-history-chart.tsx.ejs} +0 -0
  121. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-config.tsx.ejs → core.mail-config.tsx.ejs} +0 -0
  122. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-card.tsx.ejs → core.mail-sent-card.tsx.ejs} +0 -0
  123. /package/hedhog/frontend/app/dashboard/components/widgets/{mail-sent-chart.tsx.ejs → core.mail-sent-chart.tsx.ejs} +0 -0
  124. /package/hedhog/frontend/app/dashboard/components/widgets/{menus-card.tsx.ejs → core.menus-card.tsx.ejs} +0 -0
  125. /package/hedhog/frontend/app/dashboard/components/widgets/{oauth-config.tsx.ejs → core.oauth-config.tsx.ejs} +0 -0
  126. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-card.tsx.ejs → core.permissions-card.tsx.ejs} +0 -0
  127. /package/hedhog/frontend/app/dashboard/components/widgets/{permissions-chart.tsx.ejs → core.permissions-chart.tsx.ejs} +0 -0
  128. /package/hedhog/frontend/app/dashboard/components/widgets/{profile-card.tsx.ejs → core.profile-card.tsx.ejs} +0 -0
  129. /package/hedhog/frontend/app/dashboard/components/widgets/{routes-card.tsx.ejs → core.routes-card.tsx.ejs} +0 -0
  130. /package/hedhog/frontend/app/dashboard/components/widgets/{session-activity-chart.tsx.ejs → core.session-activity-chart.tsx.ejs} +0 -0
  131. /package/hedhog/frontend/app/dashboard/components/widgets/{sessions-today-card.tsx.ejs → core.sessions-today-card.tsx.ejs} +0 -0
  132. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-access-level.tsx.ejs → core.stat-access-level.tsx.ejs} +0 -0
  133. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-actions-today.tsx.ejs → core.stat-actions-today.tsx.ejs} +0 -0
  134. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-consecutive-days.tsx.ejs → core.stat-consecutive-days.tsx.ejs} +0 -0
  135. /package/hedhog/frontend/app/dashboard/components/widgets/{stat-online-time.tsx.ejs → core.stat-online-time.tsx.ejs} +0 -0
  136. /package/hedhog/frontend/app/dashboard/components/widgets/{storage-config.tsx.ejs → core.storage-config.tsx.ejs} +0 -0
  137. /package/hedhog/frontend/app/dashboard/components/widgets/{theme-config.tsx.ejs → core.theme-config.tsx.ejs} +0 -0
  138. /package/hedhog/frontend/app/dashboard/components/widgets/{user-growth-chart.tsx.ejs → core.user-growth-chart.tsx.ejs} +0 -0
  139. /package/hedhog/frontend/app/dashboard/components/widgets/{user-roles.tsx.ejs → core.user-roles.tsx.ejs} +0 -0
@@ -2,11 +2,15 @@ import { getLocaleText } from '@hed-hog/api-locale';
2
2
  import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
3
3
  import { PrismaService } from '@hed-hog/api-prisma';
4
4
  import {
5
+ BadRequestException,
6
+ ForbiddenException,
5
7
  forwardRef,
6
8
  Inject,
7
9
  Injectable,
8
10
  NotFoundException
9
11
  } from '@nestjs/common';
12
+ import { existsSync, promises as fs } from 'fs';
13
+ import { join, resolve } from 'path';
10
14
  import {
11
15
  CreateDashboardComponentDTO,
12
16
  UpdateDashboardComponentDTO,
@@ -21,7 +25,7 @@ export class DashboardComponentService {
21
25
  ) {}
22
26
 
23
27
  async getAllComponents(paginationParams: PaginationDTO) {
24
- const fields = ['slug']
28
+ const fields = ['slug', 'library_slug']
25
29
  const OR = this.prismaService.createInsensitiveSearch(
26
30
  fields,
27
31
  paginationParams,
@@ -83,6 +87,42 @@ export class DashboardComponentService {
83
87
 
84
88
  const userRoleIds = userRoles.map((ur) => ur.role_id);
85
89
 
90
+ if (userRoleIds.length === 0) {
91
+ return this.paginationService.paginate(
92
+ this.prismaService.dashboard_component,
93
+ paginationParams,
94
+ {
95
+ include: {
96
+ dashboard_component_locale: {
97
+ include: {
98
+ locale: true,
99
+ },
100
+ },
101
+ dashboard_component_role: {
102
+ include: {
103
+ role: {
104
+ include: {
105
+ role_locale: {
106
+ include: {
107
+ locale: true,
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ },
114
+ },
115
+ where: {
116
+ id: -1,
117
+ },
118
+ orderBy: {
119
+ created_at: 'desc',
120
+ },
121
+ },
122
+ 'dashboardComponent',
123
+ );
124
+ }
125
+
86
126
  const fields = ['slug']
87
127
  const OR = this.prismaService.createInsensitiveSearch(
88
128
  fields,
@@ -221,9 +261,15 @@ export class DashboardComponentService {
221
261
  }
222
262
 
223
263
  async createComponent(data: CreateDashboardComponentDTO, locale: string) {
264
+ const normalized = this.normalizeComponentIdentity(
265
+ data.slug,
266
+ data.library_slug,
267
+ );
268
+
224
269
  const component = await this.prismaService.dashboard_component.create({
225
270
  data: {
226
- slug: data.slug,
271
+ slug: normalized.slug,
272
+ library_slug: normalized.library_slug,
227
273
  min_width: data.min_width,
228
274
  max_width: data.max_width,
229
275
  min_height: data.min_height,
@@ -257,10 +303,18 @@ export class DashboardComponentService {
257
303
  }
258
304
 
259
305
  async updateComponent(id: number, data: UpdateDashboardComponentDTO, locale: string) {
306
+ const normalized = data.slug
307
+ ? this.normalizeComponentIdentity(data.slug, data.library_slug)
308
+ : {
309
+ slug: undefined,
310
+ library_slug: data.library_slug,
311
+ };
312
+
260
313
  await this.prismaService.dashboard_component.update({
261
314
  where: { id },
262
315
  data: {
263
- slug: data.slug,
316
+ slug: normalized.slug,
317
+ library_slug: normalized.library_slug,
264
318
  min_width: data.min_width,
265
319
  max_width: data.max_width,
266
320
  min_height: data.min_height,
@@ -312,4 +366,97 @@ export class DashboardComponentService {
312
366
  where: { id },
313
367
  });
314
368
  }
369
+
370
+ private normalizeComponentIdentity(slug: string, librarySlug?: string | null) {
371
+ const slugParts = slug.split('.').filter(Boolean);
372
+ const baseSlug =
373
+ slugParts.length > 0 ? slugParts[slugParts.length - 1]! : slug;
374
+ const resolvedLibrarySlug =
375
+ librarySlug || (slugParts.length > 1 ? slugParts[0]! : 'core');
376
+
377
+ return {
378
+ slug: baseSlug,
379
+ library_slug: resolvedLibrarySlug,
380
+ };
381
+ }
382
+
383
+ private buildWidgetAssetSlug(slug: string, librarySlug?: string | null) {
384
+ const normalized = this.normalizeComponentIdentity(slug, librarySlug);
385
+
386
+ return normalized.slug;
387
+ }
388
+
389
+ private resolvePreviewDirectory(): string {
390
+ const cwd = process.cwd();
391
+ const candidates = [
392
+ resolve(cwd, 'libraries/core/hedhog/frontend/public/dashboard-previews'),
393
+ resolve(cwd, '../libraries/core/hedhog/frontend/public/dashboard-previews'),
394
+ resolve(cwd, '../../libraries/core/hedhog/frontend/public/dashboard-previews'),
395
+ ];
396
+
397
+ for (const candidate of candidates) {
398
+ const parentDirectory = resolve(candidate, '..');
399
+ if (existsSync(parentDirectory)) {
400
+ return candidate;
401
+ }
402
+ }
403
+
404
+ return candidates[0];
405
+ }
406
+
407
+ async savePreview(id: number, file: MulterFile, locale: string) {
408
+ const isDevelopment = process.env.NODE_ENV !== 'production';
409
+ if (!isDevelopment) {
410
+ throw new ForbiddenException(
411
+ getLocaleText('dashboardPreviewOnlyInDevelopment', locale, 'Preview capture is only available in development environment'),
412
+ );
413
+ }
414
+
415
+ if (!file || !file.buffer) {
416
+ throw new BadRequestException(
417
+ getLocaleText('dashboardPreviewFileRequired', locale, 'Preview image file is required'),
418
+ );
419
+ }
420
+
421
+ if (!file.mimetype?.startsWith('image/')) {
422
+ throw new BadRequestException(
423
+ getLocaleText('dashboardPreviewInvalidFileType', locale, 'Preview file must be an image'),
424
+ );
425
+ }
426
+
427
+ const component = await this.prismaService.dashboard_component.findUnique({
428
+ where: { id },
429
+ select: {
430
+ id: true,
431
+ slug: true,
432
+ library_slug: true,
433
+ },
434
+ });
435
+
436
+ if (!component) {
437
+ throw new NotFoundException(
438
+ getLocaleText('dashboardComponentNotFound', locale, 'Dashboard component not found'),
439
+ );
440
+ }
441
+
442
+ const previewDirectory = this.resolvePreviewDirectory();
443
+ await fs.mkdir(previewDirectory, { recursive: true });
444
+
445
+ const assetSlug = this.buildWidgetAssetSlug(
446
+ component.slug,
447
+ component.library_slug,
448
+ );
449
+ const fileName = `${assetSlug}.png`;
450
+ const outputPath = join(previewDirectory, fileName);
451
+ await fs.writeFile(outputPath, file.buffer);
452
+
453
+ return {
454
+ success: true,
455
+ componentId: component.id,
456
+ slug: component.slug,
457
+ library_slug: component.library_slug,
458
+ fileName,
459
+ relativeUrl: `/libraries/${component.library_slug}/dashboard-previews/${fileName}`,
460
+ };
461
+ }
315
462
  }
@@ -5,6 +5,10 @@ export class CreateDTO {
5
5
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
6
6
  slug: string;
7
7
 
8
+ @IsOptional()
9
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
10
+ library_slug?: string;
11
+
8
12
  @IsOptional()
9
13
  @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
10
14
  min_width?: number;
@@ -6,6 +6,10 @@ export class UpdateDTO {
6
6
  @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
7
7
  slug?: string;
8
8
 
9
+ @IsOptional()
10
+ @IsString({ message: (args) => getLocaleText('validation.stringRequired', args.value) })
11
+ library_slug?: string;
12
+
9
13
  @IsOptional()
10
14
  @IsNumber({}, { message: (args) => getLocaleText('validation.numberRequired', args.value) })
11
15
  min_width?: number;
@@ -1014,6 +1014,7 @@ export class DashboardCoreService {
1014
1014
  i: `widget-${item.id}`,
1015
1015
  component_id: item.component_id,
1016
1016
  slug: component.slug,
1017
+ library_slug: component.library_slug,
1017
1018
  name: locale?.name || component.slug,
1018
1019
  description: locale?.description || '',
1019
1020
  x: item.x_axis,
@@ -1113,8 +1114,50 @@ export class DashboardCoreService {
1113
1114
  throw new ForbiddenException('Access denied to this dashboard');
1114
1115
  }
1115
1116
 
1117
+ const userRoles = await this.prismaService.role_user.findMany({
1118
+ where: { user_id: userId },
1119
+ select: { role_id: true },
1120
+ });
1121
+
1122
+ const userRoleIds = userRoles.map((item) => item.role_id);
1123
+
1124
+ if (userRoleIds.length === 0) {
1125
+ throw new ForbiddenException('Access denied to this component');
1126
+ }
1127
+
1128
+ const slugParts = componentSlug.split('.').filter(Boolean);
1129
+ const requestedSlug =
1130
+ slugParts.length > 0 ? slugParts[slugParts.length - 1]! : componentSlug;
1131
+ const requestedLibrarySlug =
1132
+ slugParts.length > 1 ? slugParts[0] : undefined;
1133
+
1116
1134
  const component = await this.prismaService.dashboard_component.findFirst({
1117
- where: { slug: componentSlug },
1135
+ where: {
1136
+ AND: [
1137
+ requestedLibrarySlug
1138
+ ? {
1139
+ OR: [
1140
+ { slug: componentSlug },
1141
+ {
1142
+ slug: requestedSlug,
1143
+ library_slug: requestedLibrarySlug,
1144
+ },
1145
+ ],
1146
+ }
1147
+ : {
1148
+ OR: [{ slug: componentSlug }, { slug: requestedSlug }],
1149
+ },
1150
+ {
1151
+ dashboard_component_role: {
1152
+ some: {
1153
+ role_id: {
1154
+ in: userRoleIds,
1155
+ },
1156
+ },
1157
+ },
1158
+ },
1159
+ ],
1160
+ },
1118
1161
  include: {
1119
1162
  dashboard_component_locale: {
1120
1163
  where: {
@@ -1127,7 +1170,7 @@ export class DashboardCoreService {
1127
1170
  });
1128
1171
 
1129
1172
  if (!component) {
1130
- throw new Error(`Component with slug '${componentSlug}' not found`);
1173
+ throw new ForbiddenException('Access denied to this component');
1131
1174
  }
1132
1175
 
1133
1176
  let dashboardItem = await this.prismaService.dashboard_item.findFirst({
@@ -1138,6 +1181,21 @@ export class DashboardCoreService {
1138
1181
  });
1139
1182
 
1140
1183
  if (!dashboardItem) {
1184
+ const dashboardItems = await this.prismaService.dashboard_item.findMany({
1185
+ where: {
1186
+ dashboard_id: dashboard.id,
1187
+ },
1188
+ select: {
1189
+ y_axis: true,
1190
+ height: true,
1191
+ },
1192
+ });
1193
+
1194
+ const nextAvailableY = dashboardItems.reduce(
1195
+ (maxY, item) => Math.max(maxY, item.y_axis + item.height),
1196
+ 0,
1197
+ );
1198
+
1141
1199
  dashboardItem = await this.prismaService.dashboard_item.create({
1142
1200
  data: {
1143
1201
  dashboard_id: dashboard.id,
@@ -1145,7 +1203,7 @@ export class DashboardCoreService {
1145
1203
  width: component.width,
1146
1204
  height: component.height,
1147
1205
  x_axis: 0,
1148
- y_axis: 0,
1206
+ y_axis: nextAvailableY,
1149
1207
  },
1150
1208
  });
1151
1209
  }
@@ -1156,6 +1214,7 @@ export class DashboardCoreService {
1156
1214
  i: `widget-${dashboardItem.id}`,
1157
1215
  component_id: component.id,
1158
1216
  slug: component.slug,
1217
+ library_slug: component.library_slug,
1159
1218
  name: locale?.name || component.slug,
1160
1219
  description: locale?.description || '',
1161
1220
  x: dashboardItem.x_axis,
@@ -1175,8 +1234,52 @@ export class DashboardCoreService {
1175
1234
  slug: string,
1176
1235
  widgetId: string,
1177
1236
  ) {
1178
-
1179
- throw new Error('Not implemented yet');
1237
+ const dashboard = await this.prismaService.dashboard.findFirst({
1238
+ where: { slug },
1239
+ });
1240
+
1241
+ if (!dashboard) {
1242
+ throw new Error(`Dashboard with slug '${slug}' not found`);
1243
+ }
1244
+
1245
+ const canAccess = await this.prismaService.dashboard_user.findFirst({
1246
+ where: {
1247
+ dashboard_id: dashboard.id,
1248
+ user_id: userId,
1249
+ },
1250
+ select: { id: true },
1251
+ });
1252
+
1253
+ if (!canAccess) {
1254
+ throw new ForbiddenException('Access denied to this dashboard');
1255
+ }
1256
+
1257
+ const parsedWidgetId = Number(widgetId.replace(/^widget-/, ''));
1258
+
1259
+ if (!Number.isInteger(parsedWidgetId) || parsedWidgetId <= 0) {
1260
+ throw new BadRequestException('Invalid widget id');
1261
+ }
1262
+
1263
+ const dashboardItem = await this.prismaService.dashboard_item.findFirst({
1264
+ where: {
1265
+ id: parsedWidgetId,
1266
+ dashboard_id: dashboard.id,
1267
+ },
1268
+ select: { id: true },
1269
+ });
1270
+
1271
+ if (!dashboardItem) {
1272
+ throw new BadRequestException('Widget not found in this dashboard');
1273
+ }
1274
+
1275
+ await this.prismaService.dashboard_item.delete({
1276
+ where: { id: dashboardItem.id },
1277
+ });
1278
+
1279
+ return {
1280
+ success: true,
1281
+ removedWidgetId: `widget-${dashboardItem.id}`,
1282
+ };
1180
1283
  }
1181
1284
 
1182
1285
  async checkDashboardAccess(userId: number, slug: string, locale: string) {