Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
H
hrsystem
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
hrsystem
Commits
a791a680
Commit
a791a680
authored
Apr 01, 2026
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update 7 files via Son of Anton
parent
1a902ad7
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1895 additions
and
0 deletions
+1895
-0
app.module.ts
backend/src/app.module.ts
+5
-0
analytics.controller.ts
backend/src/modules/analytics/analytics.controller.ts
+181
-0
analytics.module.ts
backend/src/modules/analytics/analytics.module.ts
+12
-0
analytics.service.ts
backend/src/modules/analytics/analytics.service.ts
+943
-0
data-export.service.ts
backend/src/modules/analytics/data-export.service.ts
+320
-0
analytics-filter.dto.ts
backend/src/modules/analytics/dto/analytics-filter.dto.ts
+100
-0
report-builder.service.ts
backend/src/modules/analytics/report-builder.service.ts
+334
-0
No files found.
backend/src/app.module.ts
View file @
a791a680
...
...
@@ -55,6 +55,9 @@ import { MeetingsModule } from './modules/meetings/meetings.module';
// ─── Phase 2D: Reports & Daily Operations ───────────────────
import
{
ReportsModule
}
from
'./modules/reports/reports.module'
;
// ─── Phase 3A: Admin & Intelligence ─────────────────────────
import
{
AnalyticsModule
}
from
'./modules/analytics/analytics.module'
;
import
{
JwtAuthGuard
}
from
'./common/guards/jwt-auth.guard'
;
import
{
RolesGuard
}
from
'./common/guards/roles.guard'
;
import
{
TransformInterceptor
}
from
'./common/interceptors/transform.interceptor'
;
...
...
@@ -108,6 +111,8 @@ import { RateLimitMiddleware } from './common/middleware/rate-limit.middleware';
MeetingsModule
,
// Phase 2D
ReportsModule
,
// Phase 3A
AnalyticsModule
,
],
providers
:
[
{
provide
:
APP_GUARD
,
useClass
:
JwtAuthGuard
},
...
...
backend/src/modules/analytics/analytics.controller.ts
0 → 100644
View file @
a791a680
import
{
Controller
,
Get
,
Post
,
Body
,
Param
,
Query
,
Res
,
HttpCode
,
HttpStatus
,
}
from
'@nestjs/common'
;
import
{
Response
}
from
'express'
;
import
{
AnalyticsService
}
from
'./analytics.service'
;
import
{
ReportBuilderService
}
from
'./report-builder.service'
;
import
{
DataExportService
}
from
'./data-export.service'
;
import
{
AnalyticsFilterDto
,
ReportBuilderQueryDto
,
ExportRequestDto
}
from
'./dto/analytics-filter.dto'
;
import
{
CurrentUser
,
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
import
{
Roles
}
from
'../../common/decorators/roles.decorator'
;
@
Controller
(
'analytics'
)
export
class
AnalyticsController
{
constructor
(
private
readonly
analyticsService
:
AnalyticsService
,
private
readonly
reportBuilderService
:
ReportBuilderService
,
private
readonly
dataExportService
:
DataExportService
,
)
{}
// ─── DASHBOARDS ──────────────────────────────────────────
@
Get
(
'dashboard'
)
async
getDashboard
(@
CurrentUser
()
user
:
RequestUser
)
{
switch
(
user
.
role
)
{
case
'SUPER_ADMIN'
:
return
this
.
analyticsService
.
getSuperAdminDashboard
();
case
'ADMIN'
:
return
this
.
analyticsService
.
getAdminDashboard
();
case
'TEAM_LEAD'
:
return
this
.
analyticsService
.
getProjectLeaderDashboard
(
user
.
id
);
case
'CONTRACTOR'
:
return
this
.
analyticsService
.
getContractorDashboard
(
user
.
id
);
default
:
return
{};
}
}
@
Get
(
'dashboard/contractor'
)
async
getContractorDashboard
(@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
analyticsService
.
getContractorDashboard
(
user
.
id
);
}
@
Get
(
'dashboard/contractor/:userId'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getContractorDashboardAdmin
(@
Param
(
'userId'
)
userId
:
string
)
{
return
this
.
analyticsService
.
getContractorDashboard
(
userId
);
}
@
Get
(
'dashboard/project-leader'
)
@
Roles
(
'SUPER_ADMIN'
,
'TEAM_LEAD'
)
async
getProjectLeaderDashboard
(@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
analyticsService
.
getProjectLeaderDashboard
(
user
.
id
);
}
@
Get
(
'dashboard/admin'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getAdminDashboard
()
{
return
this
.
analyticsService
.
getAdminDashboard
();
}
@
Get
(
'dashboard/super-admin'
)
@
Roles
(
'SUPER_ADMIN'
)
async
getSuperAdminDashboard
()
{
return
this
.
analyticsService
.
getSuperAdminDashboard
();
}
// ─── ANALYTICS ENDPOINTS ─────────────────────────────────
@
Get
(
'deductions'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
)
async
getDeductionAnalytics
(@
Query
()
filter
:
AnalyticsFilterDto
)
{
return
this
.
analyticsService
.
getDeductionAnalytics
(
filter
);
}
@
Get
(
'tasks'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
,
'TEAM_LEAD'
)
async
getTaskAnalytics
(@
Query
()
filter
:
AnalyticsFilterDto
)
{
return
this
.
analyticsService
.
getTaskAnalytics
(
filter
);
}
@
Get
(
'system-health'
)
@
Roles
(
'SUPER_ADMIN'
)
async
getSystemHealth
()
{
return
this
.
analyticsService
.
getSystemHealth
();
}
// ─── CUSTOM REPORT BUILDER ───────────────────────────────
@
Post
(
'report-builder'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
,
'TEAM_LEAD'
)
@
HttpCode
(
HttpStatus
.
OK
)
async
executeReportQuery
(@
Body
()
query
:
ReportBuilderQueryDto
,
@
CurrentUser
()
user
:
RequestUser
)
{
return
this
.
reportBuilderService
.
executeQuery
(
query
,
user
);
}
// ─── DATA EXPORT ─────────────────────────────────────────
@
Post
(
'export'
)
@
Roles
(
'SUPER_ADMIN'
,
'ADMIN'
,
'TEAM_LEAD'
)
async
exportData
(
@
Body
()
dto
:
ExportRequestDto
,
@
CurrentUser
()
user
:
RequestUser
,
@
Res
()
res
:
Response
,
)
{
const
result
=
await
this
.
dataExportService
.
exportData
(
dto
,
user
);
const
format
=
dto
.
format
||
'CSV'
;
if
(
format
===
'JSON'
)
{
res
.
setHeader
(
'Content-Type'
,
'application/json'
);
res
.
setHeader
(
'Content-Disposition'
,
`attachment; filename=
${
result
.
filename
}
.json`
);
res
.
send
(
JSON
.
stringify
(
result
.
data
,
null
,
2
));
}
else
{
// CSV
const
csv
=
this
.
convertToCSV
(
result
.
data
);
res
.
setHeader
(
'Content-Type'
,
'text/csv'
);
res
.
setHeader
(
'Content-Disposition'
,
`attachment; filename=
${
result
.
filename
}
.csv`
);
res
.
send
(
csv
);
}
}
@
Get
(
'export/contractor/:userId'
)
@
Roles
(
'SUPER_ADMIN'
)
async
exportContractorPackage
(
@
Param
(
'userId'
)
userId
:
string
,
@
CurrentUser
()
user
:
RequestUser
,
@
Res
()
res
:
Response
,
)
{
const
data
=
await
this
.
dataExportService
.
exportContractorPackage
(
userId
,
user
);
res
.
setHeader
(
'Content-Type'
,
'application/json'
);
res
.
setHeader
(
'Content-Disposition'
,
`attachment; filename=contractor-
${
userId
}
-
${
new
Date
().
toISOString
().
split
(
'T'
)[
0
]}
.json`
);
res
.
send
(
JSON
.
stringify
(
data
,
null
,
2
));
}
private
convertToCSV
(
data
:
any
[]):
string
{
if
(
!
data
||
data
.
length
===
0
)
return
''
;
const
flattenObject
=
(
obj
:
any
,
prefix
=
''
):
Record
<
string
,
any
>
=>
{
const
flat
:
Record
<
string
,
any
>
=
{};
for
(
const
[
key
,
value
]
of
Object
.
entries
(
obj
))
{
const
fullKey
=
prefix
?
`
${
prefix
}
.
${
key
}
`
:
key
;
if
(
value
!==
null
&&
typeof
value
===
'object'
&&
!
Array
.
isArray
(
value
)
&&
!
(
value
instanceof
Date
))
{
Object
.
assign
(
flat
,
flattenObject
(
value
,
fullKey
));
}
else
if
(
Array
.
isArray
(
value
))
{
flat
[
fullKey
]
=
value
.
map
((
v
)
=>
(
typeof
v
===
'object'
?
JSON
.
stringify
(
v
)
:
v
)).
join
(
'; '
);
}
else
{
flat
[
fullKey
]
=
value
;
}
}
return
flat
;
};
const
flatData
=
data
.
map
((
row
)
=>
flattenObject
(
row
));
const
allKeys
=
new
Set
<
string
>
();
flatData
.
forEach
((
row
)
=>
Object
.
keys
(
row
).
forEach
((
k
)
=>
allKeys
.
add
(
k
)));
const
headers
=
Array
.
from
(
allKeys
);
const
escapeCSV
=
(
val
:
any
):
string
=>
{
if
(
val
===
null
||
val
===
undefined
)
return
''
;
const
str
=
String
(
val
);
if
(
str
.
includes
(
','
)
||
str
.
includes
(
'"'
)
||
str
.
includes
(
'
\
n'
))
{
return
`"
${
str
.
replace
(
/"/g
,
'""'
)}
"`
;
}
return
str
;
};
const
rows
=
[
headers
.
join
(
','
),
...
flatData
.
map
((
row
)
=>
headers
.
map
((
h
)
=>
escapeCSV
(
row
[
h
])).
join
(
','
)),
];
return
rows
.
join
(
'
\
n'
);
}
}
\ No newline at end of file
backend/src/modules/analytics/analytics.module.ts
0 → 100644
View file @
a791a680
import
{
Module
}
from
'@nestjs/common'
;
import
{
AnalyticsController
}
from
'./analytics.controller'
;
import
{
AnalyticsService
}
from
'./analytics.service'
;
import
{
ReportBuilderService
}
from
'./report-builder.service'
;
import
{
DataExportService
}
from
'./data-export.service'
;
@
Module
({
controllers
:
[
AnalyticsController
],
providers
:
[
AnalyticsService
,
ReportBuilderService
,
DataExportService
],
exports
:
[
AnalyticsService
,
ReportBuilderService
,
DataExportService
],
})
export
class
AnalyticsModule
{}
\ No newline at end of file
backend/src/modules/analytics/analytics.service.ts
0 → 100644
View file @
a791a680
This diff is collapsed.
Click to expand it.
backend/src/modules/analytics/data-export.service.ts
0 → 100644
View file @
a791a680
import
{
Injectable
,
ForbiddenException
,
BadRequestException
,
Logger
,
}
from
'@nestjs/common'
;
import
{
PrismaService
}
from
'../../prisma/prisma.service'
;
import
{
ExportRequestDto
}
from
'./dto/analytics-filter.dto'
;
import
{
RequestUser
}
from
'../../common/decorators/current-user.decorator'
;
@
Injectable
()
export
class
DataExportService
{
private
readonly
logger
=
new
Logger
(
DataExportService
.
name
);
constructor
(
private
readonly
prisma
:
PrismaService
)
{}
async
exportData
(
dto
:
ExportRequestDto
,
currentUser
:
RequestUser
):
Promise
<
{
data
:
any
[];
filename
:
string
}
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
&&
currentUser
.
role
!==
'ADMIN'
)
{
if
(
currentUser
.
role
===
'TEAM_LEAD'
)
{
const
allowedEntities
=
[
'CARDS'
];
if
(
!
allowedEntities
.
includes
(
dto
.
entityType
))
{
throw
new
ForbiddenException
(
'Project Leaders can only export card data for their boards'
);
}
}
else
{
throw
new
ForbiddenException
(
'Insufficient permissions to export data'
);
}
}
const
timestamp
=
new
Date
().
toISOString
().
split
(
'T'
)[
0
];
switch
(
dto
.
entityType
)
{
case
'CONTRACTORS'
:
return
{
data
:
await
this
.
exportContractors
(
dto
),
filename
:
`contractors-
${
timestamp
}
`
,
};
case
'CARDS'
:
return
{
data
:
await
this
.
exportCards
(
dto
,
currentUser
),
filename
:
`cards-
${
timestamp
}
`
,
};
case
'DEDUCTIONS'
:
return
{
data
:
await
this
.
exportDeductions
(
dto
),
filename
:
`deductions-
${
timestamp
}
`
,
};
case
'BOUNTIES'
:
return
{
data
:
await
this
.
exportBounties
(
dto
),
filename
:
`bounties-
${
timestamp
}
`
,
};
case
'EVALUATIONS'
:
return
{
data
:
await
this
.
exportEvaluations
(
dto
),
filename
:
`evaluations-
${
timestamp
}
`
,
};
case
'PAYROLL'
:
return
{
data
:
await
this
.
exportPayroll
(
dto
),
filename
:
`payroll-
${
timestamp
}
`
,
};
case
'ADJUSTMENTS'
:
return
{
data
:
await
this
.
exportAdjustments
(
dto
),
filename
:
`adjustments-
${
timestamp
}
`
,
};
case
'AUDIT_TRAIL'
:
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin can export audit trail'
);
}
return
{
data
:
await
this
.
exportAuditTrail
(
dto
),
filename
:
`audit-trail-
${
timestamp
}
`
,
};
default
:
throw
new
BadRequestException
(
`Unsupported entity type:
${
dto
.
entityType
}
`
);
}
}
async
exportContractorPackage
(
userId
:
string
,
currentUser
:
RequestUser
):
Promise
<
any
>
{
if
(
currentUser
.
role
!==
'SUPER_ADMIN'
)
{
throw
new
ForbiddenException
(
'Only Super Admin can export full contractor data packages'
);
}
const
user
=
await
this
.
prisma
.
user
.
findUnique
({
where
:
{
id
:
userId
},
include
:
{
contracts
:
true
,
sessions
:
{
take
:
50
,
orderBy
:
{
createdAt
:
'desc'
}
},
},
});
if
(
!
user
)
throw
new
BadRequestException
(
'Contractor not found'
);
const
deductions
=
await
this
.
prisma
.
deduction
.
findMany
({
where
:
{
userId
},
orderBy
:
{
createdAt
:
'desc'
},
});
const
bounties
=
await
this
.
prisma
.
bountyPayout
.
findMany
({
where
:
{
userId
},
orderBy
:
{
paidAt
:
'desc'
},
});
const
evaluations
=
await
this
.
prisma
.
evaluation
.
findMany
({
where
:
{
userId
},
orderBy
:
[{
year
:
'desc'
},
{
month
:
'desc'
}],
});
const
pips
=
await
this
.
prisma
.
pip
.
findMany
({
where
:
{
userId
},
include
:
{
checkIns
:
true
},
});
const
learningGoals
=
await
this
.
prisma
.
learningGoal
.
findMany
({
where
:
{
userId
},
});
const
adjustments
=
await
this
.
prisma
.
adjustment
.
findMany
({
where
:
{
userId
},
orderBy
:
{
createdAt
:
'desc'
},
});
const
unavailability
=
await
this
.
prisma
.
unavailability
.
findMany
({
where
:
{
userId
},
orderBy
:
{
startDate
:
'desc'
},
});
const
payrollLines
=
await
this
.
prisma
.
payrollLine
.
findMany
({
where
:
{
userId
},
orderBy
:
{
createdAt
:
'desc'
},
});
// Strip password hash from user data
const
{
passwordHash
,
...
safeUser
}
=
user
;
return
{
exportDate
:
new
Date
().
toISOString
(),
contractor
:
safeUser
,
deductions
,
bounties
,
evaluations
,
pips
,
learningGoals
,
adjustments
,
unavailability
,
payrollLines
,
};
}
private
async
exportContractors
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
const
where
:
any
=
{
role
:
'CONTRACTOR'
,
deletedAt
:
null
};
if
(
dto
.
userId
)
where
.
id
=
dto
.
userId
;
return
this
.
prisma
.
user
.
findMany
({
where
,
select
:
{
id
:
true
,
firstName
:
true
,
lastName
:
true
,
username
:
true
,
email
:
true
,
contractorType
:
true
,
status
:
true
,
actualSalaryPiasters
:
true
,
baseSalaryPiasters
:
true
,
currentStreak
:
true
,
bestStreak
:
true
,
createdAt
:
true
,
activatedAt
:
true
,
lastLoginAt
:
true
,
},
orderBy
:
{
createdAt
:
'desc'
},
take
:
10000
,
});
}
private
async
exportCards
(
dto
:
ExportRequestDto
,
currentUser
:
RequestUser
):
Promise
<
any
[]
>
{
const
where
:
any
=
{
deletedAt
:
null
};
if
(
dto
.
dateFrom
||
dto
.
dateTo
)
{
where
.
createdAt
=
{};
if
(
dto
.
dateFrom
)
where
.
createdAt
.
gte
=
new
Date
(
dto
.
dateFrom
);
if
(
dto
.
dateTo
)
where
.
createdAt
.
lte
=
new
Date
(
dto
.
dateTo
);
}
if
(
currentUser
.
role
===
'TEAM_LEAD'
)
{
const
plBoards
=
await
this
.
prisma
.
boardMember
.
findMany
({
where
:
{
userId
:
currentUser
.
id
},
select
:
{
boardId
:
true
},
});
where
.
column
=
{
boardId
:
{
in
:
plBoards
.
map
((
b
)
=>
b
.
boardId
)
}
};
}
return
this
.
prisma
.
card
.
findMany
({
where
,
select
:
{
id
:
true
,
cardNumber
:
true
,
title
:
true
,
priority
:
true
,
dueDate
:
true
,
completedAt
:
true
,
bountyPiasters
:
true
,
estimatedHours
:
true
,
actualHours
:
true
,
leadTimeHours
:
true
,
cycleTimeHours
:
true
,
frozenTimeHours
:
true
,
isArchived
:
true
,
createdAt
:
true
,
column
:
{
select
:
{
name
:
true
,
type
:
true
,
board
:
{
select
:
{
name
:
true
,
key
:
true
}
}
}
},
assignees
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
},
},
orderBy
:
{
createdAt
:
'desc'
},
take
:
50000
,
});
}
private
async
exportDeductions
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
const
where
:
any
=
{};
if
(
dto
.
userId
)
where
.
userId
=
dto
.
userId
;
if
(
dto
.
dateFrom
||
dto
.
dateTo
)
{
where
.
violationDate
=
{};
if
(
dto
.
dateFrom
)
where
.
violationDate
.
gte
=
new
Date
(
dto
.
dateFrom
);
if
(
dto
.
dateTo
)
where
.
violationDate
.
lte
=
new
Date
(
dto
.
dateTo
);
}
return
this
.
prisma
.
deduction
.
findMany
({
where
,
include
:
{
user
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
},
initiatedBy
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
},
},
orderBy
:
{
createdAt
:
'desc'
},
take
:
50000
,
});
}
private
async
exportBounties
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
const
where
:
any
=
{};
if
(
dto
.
userId
)
where
.
userId
=
dto
.
userId
;
if
(
dto
.
dateFrom
||
dto
.
dateTo
)
{
where
.
paidAt
=
{};
if
(
dto
.
dateFrom
)
where
.
paidAt
.
gte
=
new
Date
(
dto
.
dateFrom
);
if
(
dto
.
dateTo
)
where
.
paidAt
.
lte
=
new
Date
(
dto
.
dateTo
);
}
return
this
.
prisma
.
bountyPayout
.
findMany
({
where
,
orderBy
:
{
paidAt
:
'desc'
},
take
:
50000
,
});
}
private
async
exportEvaluations
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
const
where
:
any
=
{};
if
(
dto
.
userId
)
where
.
userId
=
dto
.
userId
;
return
this
.
prisma
.
evaluation
.
findMany
({
where
,
include
:
{
user
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
}
},
orderBy
:
[{
year
:
'desc'
},
{
month
:
'desc'
}],
take
:
50000
,
});
}
private
async
exportPayroll
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
return
this
.
prisma
.
payroll
.
findMany
({
orderBy
:
[{
year
:
'desc'
},
{
month
:
'desc'
}],
include
:
{
lines
:
{
include
:
{
user
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
},
},
},
},
take
:
1000
,
});
}
private
async
exportAdjustments
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
const
where
:
any
=
{};
if
(
dto
.
userId
)
where
.
userId
=
dto
.
userId
;
if
(
dto
.
dateFrom
||
dto
.
dateTo
)
{
where
.
createdAt
=
{};
if
(
dto
.
dateFrom
)
where
.
createdAt
.
gte
=
new
Date
(
dto
.
dateFrom
);
if
(
dto
.
dateTo
)
where
.
createdAt
.
lte
=
new
Date
(
dto
.
dateTo
);
}
return
this
.
prisma
.
adjustment
.
findMany
({
where
,
include
:
{
user
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
},
createdBy
:
{
select
:
{
firstName
:
true
,
lastName
:
true
}
},
},
orderBy
:
{
createdAt
:
'desc'
},
take
:
50000
,
});
}
private
async
exportAuditTrail
(
dto
:
ExportRequestDto
):
Promise
<
any
[]
>
{
const
where
:
any
=
{};
if
(
dto
.
userId
)
where
.
userId
=
dto
.
userId
;
if
(
dto
.
dateFrom
||
dto
.
dateTo
)
{
where
.
createdAt
=
{};
if
(
dto
.
dateFrom
)
where
.
createdAt
.
gte
=
new
Date
(
dto
.
dateFrom
);
if
(
dto
.
dateTo
)
where
.
createdAt
.
lte
=
new
Date
(
dto
.
dateTo
);
}
return
this
.
prisma
.
auditTrail
.
findMany
({
where
,
include
:
{
user
:
{
select
:
{
firstName
:
true
,
lastName
:
true
,
username
:
true
}
},
},
orderBy
:
{
createdAt
:
'desc'
},
take
:
50000
,
});
}
}
\ No newline at end of file
backend/src/modules/analytics/dto/analytics-filter.dto.ts
0 → 100644
View file @
a791a680
import
{
IsOptional
,
IsString
,
IsDateString
,
IsInt
,
IsArray
}
from
'class-validator'
;
import
{
Type
}
from
'class-transformer'
;
import
{
PaginationDto
}
from
'../../../common/dto/pagination.dto'
;
export
class
AnalyticsFilterDto
{
@
IsOptional
()
@
IsDateString
()
dateFrom
?:
string
;
@
IsOptional
()
@
IsDateString
()
dateTo
?:
string
;
@
IsOptional
()
@
IsString
()
userId
?:
string
;
@
IsOptional
()
@
IsString
()
boardId
?:
string
;
@
IsOptional
()
@
Type
(()
=>
Number
)
@
IsInt
()
month
?:
number
;
@
IsOptional
()
@
Type
(()
=>
Number
)
@
IsInt
()
year
?:
number
;
}
export
class
ReportBuilderQueryDto
extends
PaginationDto
{
@
IsString
()
dataSource
:
string
;
// CONTRACTORS, CARDS, DEDUCTIONS, BOUNTIES, EVALUATIONS, REPORTS, PAYROLL, ADJUSTMENTS, UNAVAILABILITY, LEARNING_GOALS
@
IsOptional
()
@
IsArray
()
@
IsString
({
each
:
true
})
columns
?:
string
[];
@
IsOptional
()
@
IsString
()
groupBy
?:
string
;
@
IsOptional
()
@
IsString
()
aggregation
?:
string
;
// SUM, AVG, COUNT, MIN, MAX
@
IsOptional
()
@
IsString
()
aggregationField
?:
string
;
@
IsOptional
()
@
IsDateString
()
dateFrom
?:
string
;
@
IsOptional
()
@
IsDateString
()
dateTo
?:
string
;
@
IsOptional
()
@
IsString
()
userId
?:
string
;
@
IsOptional
()
@
IsString
()
boardId
?:
string
;
@
IsOptional
()
@
IsString
()
status
?:
string
;
@
IsOptional
()
@
IsString
()
category
?:
string
;
}
export
class
ExportRequestDto
{
@
IsString
()
entityType
:
string
;
// CONTRACTORS, CARDS, DEDUCTIONS, BOUNTIES, EVALUATIONS, REPORTS, PAYROLL, ADJUSTMENTS, AUDIT_TRAIL, ALL
@
IsOptional
()
@
IsString
()
format
?:
string
;
// CSV, JSON (default: CSV)
@
IsOptional
()
@
IsDateString
()
dateFrom
?:
string
;
@
IsOptional
()
@
IsDateString
()
dateTo
?:
string
;
@
IsOptional
()
@
IsString
()
userId
?:
string
;
}
\ No newline at end of file
backend/src/modules/analytics/report-builder.service.ts
0 → 100644
View file @
a791a680
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment