feat(reporting) : add Reporting dashboard front layer
LST-59 (3.1) front. Completes the Reporting module. - New frontend/modules/reporting/ layer (auto-detected): /reporting page (admin middleware) consuming the 4 read-only report endpoints. - Filters (period presets + custom dates, project, user). 4 sections (time per project, time per user, tasks by status, absences by type) each with a DataTable + a Chart.js chart (reused global registration). - Front-side CSV export per section (useCsvExport: BOM UTF-8, ; separator). - i18n keys (reporting.*, sidebar.admin.reporting). nuxt build passes; /reporting routed; no route regression.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
export type TimePerProject = {
|
||||
projectId: number
|
||||
projectCode: string
|
||||
projectName: string
|
||||
hours: number
|
||||
entryCount: number
|
||||
}
|
||||
|
||||
export type TimePerUser = {
|
||||
userId: number
|
||||
username: string
|
||||
fullName: string
|
||||
hours: number
|
||||
entryCount: number
|
||||
}
|
||||
|
||||
export type TasksByStatus = {
|
||||
statusId: number
|
||||
statusLabel: string
|
||||
count: number
|
||||
}
|
||||
|
||||
export type AbsencesByType = {
|
||||
type: string
|
||||
count: number
|
||||
totalDays: number
|
||||
}
|
||||
|
||||
export type ReportFilters = {
|
||||
from: string
|
||||
to: string
|
||||
userId?: number | null
|
||||
projectId?: number | null
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type {
|
||||
AbsencesByType,
|
||||
ReportFilters,
|
||||
TasksByStatus,
|
||||
TimePerProject,
|
||||
TimePerUser,
|
||||
} from './dto/reporting'
|
||||
import type { AnyObject } from '~/shared/composables/useApi'
|
||||
import type { HydraCollection } from '~/utils/api'
|
||||
import { extractHydraMembers } from '~/utils/api'
|
||||
|
||||
function buildQuery(filters: Partial<ReportFilters>, keys: (keyof ReportFilters)[]): AnyObject {
|
||||
const query: AnyObject = {}
|
||||
for (const key of keys) {
|
||||
const value = filters[key]
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
query[key] = value
|
||||
}
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
export function useReportingService() {
|
||||
const api = useApi()
|
||||
|
||||
async function timePerProject(filters: ReportFilters): Promise<TimePerProject[]> {
|
||||
const query = buildQuery(filters, ['from', 'to', 'userId', 'projectId'])
|
||||
const data = await api.get<HydraCollection<TimePerProject>>('/reports/time-per-project', query, { toast: false })
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function timePerUser(filters: ReportFilters): Promise<TimePerUser[]> {
|
||||
const query = buildQuery(filters, ['from', 'to', 'projectId'])
|
||||
const data = await api.get<HydraCollection<TimePerUser>>('/reports/time-per-user', query, { toast: false })
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function tasksByStatus(filters: ReportFilters): Promise<TasksByStatus[]> {
|
||||
const query = buildQuery(filters, ['projectId'])
|
||||
const data = await api.get<HydraCollection<TasksByStatus>>('/reports/tasks-by-status', query, { toast: false })
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
async function absencesByType(filters: ReportFilters): Promise<AbsencesByType[]> {
|
||||
const query = buildQuery(filters, ['from', 'to', 'userId'])
|
||||
const data = await api.get<HydraCollection<AbsencesByType>>('/reports/absences-by-type', query, { toast: false })
|
||||
return extractHydraMembers(data)
|
||||
}
|
||||
|
||||
return { timePerProject, timePerUser, tasksByStatus, absencesByType }
|
||||
}
|
||||
Reference in New Issue
Block a user