@ikatec/digisac-api-sdk 4.2.0 → 4.3.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.
- package/dist/apis/dashboard/DashboardApi.cjs +36 -1
- package/dist/apis/dashboard/DashboardApi.cjs.map +1 -1
- package/dist/apis/dashboard/DashboardApi.d.ts +55 -2
- package/dist/apis/dashboard/DashboardApi.d.ts.map +1 -1
- package/dist/apis/dashboard/DashboardApi.mjs +36 -1
- package/dist/apis/dashboard/DashboardApi.mjs.map +1 -1
- package/dist/apis/dashboard/types.d.ts +71 -0
- package/dist/apis/dashboard/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/apis/dashboard/DashboardApi.test-d.ts +49 -0
- package/src/apis/dashboard/DashboardApi.test.ts +197 -9
- package/src/apis/dashboard/DashboardApi.ts +130 -2
- package/src/apis/dashboard/types.ts +78 -0
- package/src/apis/queryFormat.test.ts +64 -0
|
@@ -4,7 +4,8 @@ const require_core_BaseApi = require('../../core/BaseApi.cjs');
|
|
|
4
4
|
//#region src/apis/dashboard/DashboardApi.ts
|
|
5
5
|
/**
|
|
6
6
|
* Service statistics ("Estatísticas de atendimento"). The `/dashboard` routes only require an
|
|
7
|
-
* authenticated user (no permission check).
|
|
7
|
+
* authenticated user (no permission check). Every route takes the same {@link DashboardQuery}
|
|
8
|
+
* filters; the grouped routes answer one item per day, user, department, topic or connection.
|
|
8
9
|
*/
|
|
9
10
|
var DashboardApi = class extends require_core_BaseApi.BaseApi {
|
|
10
11
|
basePath = "/dashboard";
|
|
@@ -12,6 +13,40 @@ var DashboardApi = class extends require_core_BaseApi.BaseApi {
|
|
|
12
13
|
getGeneral(query, headers) {
|
|
13
14
|
return this.client.get(`${this.basePath}/general${this.toQueryString(query)}`, headers);
|
|
14
15
|
}
|
|
16
|
+
getByPeriod(query, headers) {
|
|
17
|
+
return this.getGrouped("by-period", query, headers);
|
|
18
|
+
}
|
|
19
|
+
getByUser(query, headers) {
|
|
20
|
+
return this.getGrouped("by-user", query, headers);
|
|
21
|
+
}
|
|
22
|
+
getByDepartment(query, headers) {
|
|
23
|
+
return this.getGrouped("by-department", query, headers);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The counts of each ticket topic of the period (`GET /dashboard/by-topic`). Does not take
|
|
27
|
+
* `withTotals` (the backend ignores it).
|
|
28
|
+
*/
|
|
29
|
+
getByTopic(query, headers) {
|
|
30
|
+
return this.client.get(`${this.basePath}/by-topic${this.toQueryString(query)}`, headers);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The counts of each connection of the period (`GET /dashboard/by-service`). Does not take
|
|
34
|
+
* `withTotals` (the backend ignores it).
|
|
35
|
+
*/
|
|
36
|
+
getByService(query, headers) {
|
|
37
|
+
return this.client.get(`${this.basePath}/by-service${this.toQueryString(query)}`, headers);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* `withTotals` is only sent when `true`: the backend reads it with a truthiness check, so in the
|
|
41
|
+
* `qs` format a serialized `withTotals=false` (the string `'false'`) would wrap the answer anyway.
|
|
42
|
+
*/
|
|
43
|
+
getGrouped(route, query, headers) {
|
|
44
|
+
const { withTotals, ...filters } = query ?? {};
|
|
45
|
+
return this.client.get(`${this.basePath}/${route}${this.toQueryString(withTotals ? {
|
|
46
|
+
...filters,
|
|
47
|
+
withTotals: true
|
|
48
|
+
} : filters)}`, headers);
|
|
49
|
+
}
|
|
15
50
|
};
|
|
16
51
|
|
|
17
52
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardApi.cjs","names":["BaseApi"],"sources":["../../../src/apis/dashboard/DashboardApi.ts"],"sourcesContent":["import { BaseApi } from '../../core/BaseApi'\nimport type {
|
|
1
|
+
{"version":3,"file":"DashboardApi.cjs","names":["BaseApi"],"sources":["../../../src/apis/dashboard/DashboardApi.ts"],"sourcesContent":["import { BaseApi } from '../../core/BaseApi'\nimport type {\n DashboardDepartmentItem,\n DashboardGeneral,\n DashboardGrouped,\n DashboardPeriodItem,\n DashboardQuery,\n DashboardServiceItem,\n DashboardTopicItem,\n DashboardUserItem,\n DashboardWithTotals,\n} from './types'\n\n/**\n * Service statistics (\"Estatísticas de atendimento\"). The `/dashboard` routes only require an\n * authenticated user (no permission check). Every route takes the same {@link DashboardQuery}\n * filters; the grouped routes answer one item per day, user, department, topic or connection.\n */\nexport class DashboardApi extends BaseApi {\n public readonly basePath = '/dashboard'\n\n /** Message, ticket, contact and time counts of the period (`GET /dashboard/general`). */\n getGeneral(query?: DashboardQuery, headers?: Record<string, string>): Promise<DashboardGeneral> {\n return this.client.get<DashboardGeneral>(\n `${this.basePath}/general${this.toQueryString(query)}`,\n headers,\n )\n }\n\n /**\n * The counts of each bucket of the period (`GET /dashboard/by-period`): one item per day by default,\n * or per `grouping` (hour, week, month, year). With `withTotals: true` the answer is\n * `{ totals, items }`.\n */\n getByPeriod(\n query: DashboardQuery & { withTotals: true },\n headers?: Record<string, string>,\n ): Promise<DashboardGrouped<DashboardPeriodItem>>\n getByPeriod(\n query?: DashboardQuery & { withTotals?: false },\n headers?: Record<string, string>,\n ): Promise<DashboardPeriodItem[]>\n /** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */\n getByPeriod(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>>\n getByPeriod(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>> {\n return this.getGrouped<DashboardPeriodItem>('by-period', query, headers)\n }\n\n /**\n * The counts of each user of the period (`GET /dashboard/by-user`). With `withTotals: true` the\n * answer is `{ totals, items }`.\n */\n getByUser(\n query: DashboardQuery & { withTotals: true },\n headers?: Record<string, string>,\n ): Promise<DashboardGrouped<DashboardUserItem>>\n getByUser(\n query?: DashboardQuery & { withTotals?: false },\n headers?: Record<string, string>,\n ): Promise<DashboardUserItem[]>\n /** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */\n getByUser(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>>\n getByUser(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>> {\n return this.getGrouped<DashboardUserItem>('by-user', query, headers)\n }\n\n /**\n * The counts of each department of the period (`GET /dashboard/by-department`). With\n * `withTotals: true` the answer is `{ totals, items }`.\n */\n getByDepartment(\n query: DashboardQuery & { withTotals: true },\n headers?: Record<string, string>,\n ): Promise<DashboardGrouped<DashboardDepartmentItem>>\n getByDepartment(\n query?: DashboardQuery & { withTotals?: false },\n headers?: Record<string, string>,\n ): Promise<DashboardDepartmentItem[]>\n /** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */\n getByDepartment(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>>\n getByDepartment(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>> {\n return this.getGrouped<DashboardDepartmentItem>('by-department', query, headers)\n }\n\n /**\n * The counts of each ticket topic of the period (`GET /dashboard/by-topic`). Does not take\n * `withTotals` (the backend ignores it).\n */\n getByTopic(\n query?: DashboardQuery,\n headers?: Record<string, string>,\n ): Promise<DashboardTopicItem[]> {\n return this.client.get<DashboardTopicItem[]>(\n `${this.basePath}/by-topic${this.toQueryString(query)}`,\n headers,\n )\n }\n\n /**\n * The counts of each connection of the period (`GET /dashboard/by-service`). Does not take\n * `withTotals` (the backend ignores it).\n */\n getByService(\n query?: DashboardQuery,\n headers?: Record<string, string>,\n ): Promise<DashboardServiceItem[]> {\n return this.client.get<DashboardServiceItem[]>(\n `${this.basePath}/by-service${this.toQueryString(query)}`,\n headers,\n )\n }\n\n /**\n * `withTotals` is only sent when `true`: the backend reads it with a truthiness check, so in the\n * `qs` format a serialized `withTotals=false` (the string `'false'`) would wrap the answer anyway.\n */\n private getGrouped<TItem>(\n route: string,\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<TItem[] | DashboardGrouped<TItem>> {\n const { withTotals, ...filters } = query ?? {}\n return this.client.get<TItem[] | DashboardGrouped<TItem>>(\n `${this.basePath}/${route}${this.toQueryString(withTotals ? { ...filters, withTotals: true } : filters)}`,\n headers,\n )\n }\n}\n"],"mappings":";;;;;;;;;AAkBA,IAAa,eAAb,cAAkCA,6BAAQ;CACxC,AAAgB,WAAW;;CAG3B,WAAW,OAAwB,SAA6D;AAC9F,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,UAAU,KAAK,cAAc,MAAM,IACpD,QACD;;CAqBH,YACE,OACA,SACwE;AACxE,SAAO,KAAK,WAAgC,aAAa,OAAO,QAAQ;;CAoB1E,UACE,OACA,SACoE;AACpE,SAAO,KAAK,WAA8B,WAAW,OAAO,QAAQ;;CAoBtE,gBACE,OACA,SACgF;AAChF,SAAO,KAAK,WAAoC,iBAAiB,OAAO,QAAQ;;;;;;CAOlF,WACE,OACA,SAC+B;AAC/B,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,WAAW,KAAK,cAAc,MAAM,IACrD,QACD;;;;;;CAOH,aACE,OACA,SACiC;AACjC,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,aAAa,KAAK,cAAc,MAAM,IACvD,QACD;;;;;;CAOH,AAAQ,WACN,OACA,OACA,SAC4C;EAC5C,MAAM,EAAE,YAAY,GAAG,YAAY,SAAS,EAAE;AAC9C,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,GAAG,QAAQ,KAAK,cAAc,aAAa;GAAE,GAAG;GAAS,YAAY;GAAM,GAAG,QAAQ,IACvG,QACD"}
|
|
@@ -1,12 +1,65 @@
|
|
|
1
1
|
import { BaseApi } from '../../core/BaseApi';
|
|
2
|
-
import type { DashboardGeneral, DashboardQuery } from './types';
|
|
2
|
+
import type { DashboardDepartmentItem, DashboardGeneral, DashboardGrouped, DashboardPeriodItem, DashboardQuery, DashboardServiceItem, DashboardTopicItem, DashboardUserItem, DashboardWithTotals } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Service statistics ("Estatísticas de atendimento"). The `/dashboard` routes only require an
|
|
5
|
-
* authenticated user (no permission check).
|
|
5
|
+
* authenticated user (no permission check). Every route takes the same {@link DashboardQuery}
|
|
6
|
+
* filters; the grouped routes answer one item per day, user, department, topic or connection.
|
|
6
7
|
*/
|
|
7
8
|
export declare class DashboardApi extends BaseApi {
|
|
8
9
|
readonly basePath = "/dashboard";
|
|
9
10
|
/** Message, ticket, contact and time counts of the period (`GET /dashboard/general`). */
|
|
10
11
|
getGeneral(query?: DashboardQuery, headers?: Record<string, string>): Promise<DashboardGeneral>;
|
|
12
|
+
/**
|
|
13
|
+
* The counts of each bucket of the period (`GET /dashboard/by-period`): one item per day by default,
|
|
14
|
+
* or per `grouping` (hour, week, month, year). With `withTotals: true` the answer is
|
|
15
|
+
* `{ totals, items }`.
|
|
16
|
+
*/
|
|
17
|
+
getByPeriod(query: DashboardQuery & {
|
|
18
|
+
withTotals: true;
|
|
19
|
+
}, headers?: Record<string, string>): Promise<DashboardGrouped<DashboardPeriodItem>>;
|
|
20
|
+
getByPeriod(query?: DashboardQuery & {
|
|
21
|
+
withTotals?: false;
|
|
22
|
+
}, headers?: Record<string, string>): Promise<DashboardPeriodItem[]>;
|
|
23
|
+
/** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */
|
|
24
|
+
getByPeriod(query?: DashboardQuery & DashboardWithTotals, headers?: Record<string, string>): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>>;
|
|
25
|
+
/**
|
|
26
|
+
* The counts of each user of the period (`GET /dashboard/by-user`). With `withTotals: true` the
|
|
27
|
+
* answer is `{ totals, items }`.
|
|
28
|
+
*/
|
|
29
|
+
getByUser(query: DashboardQuery & {
|
|
30
|
+
withTotals: true;
|
|
31
|
+
}, headers?: Record<string, string>): Promise<DashboardGrouped<DashboardUserItem>>;
|
|
32
|
+
getByUser(query?: DashboardQuery & {
|
|
33
|
+
withTotals?: false;
|
|
34
|
+
}, headers?: Record<string, string>): Promise<DashboardUserItem[]>;
|
|
35
|
+
/** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */
|
|
36
|
+
getByUser(query?: DashboardQuery & DashboardWithTotals, headers?: Record<string, string>): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>>;
|
|
37
|
+
/**
|
|
38
|
+
* The counts of each department of the period (`GET /dashboard/by-department`). With
|
|
39
|
+
* `withTotals: true` the answer is `{ totals, items }`.
|
|
40
|
+
*/
|
|
41
|
+
getByDepartment(query: DashboardQuery & {
|
|
42
|
+
withTotals: true;
|
|
43
|
+
}, headers?: Record<string, string>): Promise<DashboardGrouped<DashboardDepartmentItem>>;
|
|
44
|
+
getByDepartment(query?: DashboardQuery & {
|
|
45
|
+
withTotals?: false;
|
|
46
|
+
}, headers?: Record<string, string>): Promise<DashboardDepartmentItem[]>;
|
|
47
|
+
/** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */
|
|
48
|
+
getByDepartment(query?: DashboardQuery & DashboardWithTotals, headers?: Record<string, string>): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>>;
|
|
49
|
+
/**
|
|
50
|
+
* The counts of each ticket topic of the period (`GET /dashboard/by-topic`). Does not take
|
|
51
|
+
* `withTotals` (the backend ignores it).
|
|
52
|
+
*/
|
|
53
|
+
getByTopic(query?: DashboardQuery, headers?: Record<string, string>): Promise<DashboardTopicItem[]>;
|
|
54
|
+
/**
|
|
55
|
+
* The counts of each connection of the period (`GET /dashboard/by-service`). Does not take
|
|
56
|
+
* `withTotals` (the backend ignores it).
|
|
57
|
+
*/
|
|
58
|
+
getByService(query?: DashboardQuery, headers?: Record<string, string>): Promise<DashboardServiceItem[]>;
|
|
59
|
+
/**
|
|
60
|
+
* `withTotals` is only sent when `true`: the backend reads it with a truthiness check, so in the
|
|
61
|
+
* `qs` format a serialized `withTotals=false` (the string `'false'`) would wrap the answer anyway.
|
|
62
|
+
*/
|
|
63
|
+
private getGrouped;
|
|
11
64
|
}
|
|
12
65
|
//# sourceMappingURL=DashboardApi.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardApi.d.ts","sourceRoot":"","sources":["../../../src/apis/dashboard/DashboardApi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"DashboardApi.d.ts","sourceRoot":"","sources":["../../../src/apis/dashboard/DashboardApi.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,8 @@ import { BaseApi } from "../../core/BaseApi.mjs";
|
|
|
3
3
|
//#region src/apis/dashboard/DashboardApi.ts
|
|
4
4
|
/**
|
|
5
5
|
* Service statistics ("Estatísticas de atendimento"). The `/dashboard` routes only require an
|
|
6
|
-
* authenticated user (no permission check).
|
|
6
|
+
* authenticated user (no permission check). Every route takes the same {@link DashboardQuery}
|
|
7
|
+
* filters; the grouped routes answer one item per day, user, department, topic or connection.
|
|
7
8
|
*/
|
|
8
9
|
var DashboardApi = class extends BaseApi {
|
|
9
10
|
basePath = "/dashboard";
|
|
@@ -11,6 +12,40 @@ var DashboardApi = class extends BaseApi {
|
|
|
11
12
|
getGeneral(query, headers) {
|
|
12
13
|
return this.client.get(`${this.basePath}/general${this.toQueryString(query)}`, headers);
|
|
13
14
|
}
|
|
15
|
+
getByPeriod(query, headers) {
|
|
16
|
+
return this.getGrouped("by-period", query, headers);
|
|
17
|
+
}
|
|
18
|
+
getByUser(query, headers) {
|
|
19
|
+
return this.getGrouped("by-user", query, headers);
|
|
20
|
+
}
|
|
21
|
+
getByDepartment(query, headers) {
|
|
22
|
+
return this.getGrouped("by-department", query, headers);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The counts of each ticket topic of the period (`GET /dashboard/by-topic`). Does not take
|
|
26
|
+
* `withTotals` (the backend ignores it).
|
|
27
|
+
*/
|
|
28
|
+
getByTopic(query, headers) {
|
|
29
|
+
return this.client.get(`${this.basePath}/by-topic${this.toQueryString(query)}`, headers);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The counts of each connection of the period (`GET /dashboard/by-service`). Does not take
|
|
33
|
+
* `withTotals` (the backend ignores it).
|
|
34
|
+
*/
|
|
35
|
+
getByService(query, headers) {
|
|
36
|
+
return this.client.get(`${this.basePath}/by-service${this.toQueryString(query)}`, headers);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* `withTotals` is only sent when `true`: the backend reads it with a truthiness check, so in the
|
|
40
|
+
* `qs` format a serialized `withTotals=false` (the string `'false'`) would wrap the answer anyway.
|
|
41
|
+
*/
|
|
42
|
+
getGrouped(route, query, headers) {
|
|
43
|
+
const { withTotals, ...filters } = query ?? {};
|
|
44
|
+
return this.client.get(`${this.basePath}/${route}${this.toQueryString(withTotals ? {
|
|
45
|
+
...filters,
|
|
46
|
+
withTotals: true
|
|
47
|
+
} : filters)}`, headers);
|
|
48
|
+
}
|
|
14
49
|
};
|
|
15
50
|
|
|
16
51
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardApi.mjs","names":[],"sources":["../../../src/apis/dashboard/DashboardApi.ts"],"sourcesContent":["import { BaseApi } from '../../core/BaseApi'\nimport type {
|
|
1
|
+
{"version":3,"file":"DashboardApi.mjs","names":[],"sources":["../../../src/apis/dashboard/DashboardApi.ts"],"sourcesContent":["import { BaseApi } from '../../core/BaseApi'\nimport type {\n DashboardDepartmentItem,\n DashboardGeneral,\n DashboardGrouped,\n DashboardPeriodItem,\n DashboardQuery,\n DashboardServiceItem,\n DashboardTopicItem,\n DashboardUserItem,\n DashboardWithTotals,\n} from './types'\n\n/**\n * Service statistics (\"Estatísticas de atendimento\"). The `/dashboard` routes only require an\n * authenticated user (no permission check). Every route takes the same {@link DashboardQuery}\n * filters; the grouped routes answer one item per day, user, department, topic or connection.\n */\nexport class DashboardApi extends BaseApi {\n public readonly basePath = '/dashboard'\n\n /** Message, ticket, contact and time counts of the period (`GET /dashboard/general`). */\n getGeneral(query?: DashboardQuery, headers?: Record<string, string>): Promise<DashboardGeneral> {\n return this.client.get<DashboardGeneral>(\n `${this.basePath}/general${this.toQueryString(query)}`,\n headers,\n )\n }\n\n /**\n * The counts of each bucket of the period (`GET /dashboard/by-period`): one item per day by default,\n * or per `grouping` (hour, week, month, year). With `withTotals: true` the answer is\n * `{ totals, items }`.\n */\n getByPeriod(\n query: DashboardQuery & { withTotals: true },\n headers?: Record<string, string>,\n ): Promise<DashboardGrouped<DashboardPeriodItem>>\n getByPeriod(\n query?: DashboardQuery & { withTotals?: false },\n headers?: Record<string, string>,\n ): Promise<DashboardPeriodItem[]>\n /** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */\n getByPeriod(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>>\n getByPeriod(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>> {\n return this.getGrouped<DashboardPeriodItem>('by-period', query, headers)\n }\n\n /**\n * The counts of each user of the period (`GET /dashboard/by-user`). With `withTotals: true` the\n * answer is `{ totals, items }`.\n */\n getByUser(\n query: DashboardQuery & { withTotals: true },\n headers?: Record<string, string>,\n ): Promise<DashboardGrouped<DashboardUserItem>>\n getByUser(\n query?: DashboardQuery & { withTotals?: false },\n headers?: Record<string, string>,\n ): Promise<DashboardUserItem[]>\n /** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */\n getByUser(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>>\n getByUser(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>> {\n return this.getGrouped<DashboardUserItem>('by-user', query, headers)\n }\n\n /**\n * The counts of each department of the period (`GET /dashboard/by-department`). With\n * `withTotals: true` the answer is `{ totals, items }`.\n */\n getByDepartment(\n query: DashboardQuery & { withTotals: true },\n headers?: Record<string, string>,\n ): Promise<DashboardGrouped<DashboardDepartmentItem>>\n getByDepartment(\n query?: DashboardQuery & { withTotals?: false },\n headers?: Record<string, string>,\n ): Promise<DashboardDepartmentItem[]>\n /** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */\n getByDepartment(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>>\n getByDepartment(\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>> {\n return this.getGrouped<DashboardDepartmentItem>('by-department', query, headers)\n }\n\n /**\n * The counts of each ticket topic of the period (`GET /dashboard/by-topic`). Does not take\n * `withTotals` (the backend ignores it).\n */\n getByTopic(\n query?: DashboardQuery,\n headers?: Record<string, string>,\n ): Promise<DashboardTopicItem[]> {\n return this.client.get<DashboardTopicItem[]>(\n `${this.basePath}/by-topic${this.toQueryString(query)}`,\n headers,\n )\n }\n\n /**\n * The counts of each connection of the period (`GET /dashboard/by-service`). Does not take\n * `withTotals` (the backend ignores it).\n */\n getByService(\n query?: DashboardQuery,\n headers?: Record<string, string>,\n ): Promise<DashboardServiceItem[]> {\n return this.client.get<DashboardServiceItem[]>(\n `${this.basePath}/by-service${this.toQueryString(query)}`,\n headers,\n )\n }\n\n /**\n * `withTotals` is only sent when `true`: the backend reads it with a truthiness check, so in the\n * `qs` format a serialized `withTotals=false` (the string `'false'`) would wrap the answer anyway.\n */\n private getGrouped<TItem>(\n route: string,\n query?: DashboardQuery & DashboardWithTotals,\n headers?: Record<string, string>,\n ): Promise<TItem[] | DashboardGrouped<TItem>> {\n const { withTotals, ...filters } = query ?? {}\n return this.client.get<TItem[] | DashboardGrouped<TItem>>(\n `${this.basePath}/${route}${this.toQueryString(withTotals ? { ...filters, withTotals: true } : filters)}`,\n headers,\n )\n }\n}\n"],"mappings":";;;;;;;;AAkBA,IAAa,eAAb,cAAkC,QAAQ;CACxC,AAAgB,WAAW;;CAG3B,WAAW,OAAwB,SAA6D;AAC9F,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,UAAU,KAAK,cAAc,MAAM,IACpD,QACD;;CAqBH,YACE,OACA,SACwE;AACxE,SAAO,KAAK,WAAgC,aAAa,OAAO,QAAQ;;CAoB1E,UACE,OACA,SACoE;AACpE,SAAO,KAAK,WAA8B,WAAW,OAAO,QAAQ;;CAoBtE,gBACE,OACA,SACgF;AAChF,SAAO,KAAK,WAAoC,iBAAiB,OAAO,QAAQ;;;;;;CAOlF,WACE,OACA,SAC+B;AAC/B,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,WAAW,KAAK,cAAc,MAAM,IACrD,QACD;;;;;;CAOH,aACE,OACA,SACiC;AACjC,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,aAAa,KAAK,cAAc,MAAM,IACvD,QACD;;;;;;CAOH,AAAQ,WACN,OACA,OACA,SAC4C;EAC5C,MAAM,EAAE,YAAY,GAAG,YAAY,SAAS,EAAE;AAC9C,SAAO,KAAK,OAAO,IACjB,GAAG,KAAK,SAAS,GAAG,QAAQ,KAAK,cAAc,aAAa;GAAE,GAAG;GAAS,YAAY;GAAM,GAAG,QAAQ,IACvG,QACD"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Granularity of the by-period items (`grouping`). Default: `daily`. Whatever the granularity, the
|
|
3
|
+
* `name` of each item is its day (`dd/MM/yyyy`), so with `hourly` the items of one day share the same
|
|
4
|
+
* `name` (they come in chronological order).
|
|
5
|
+
*/
|
|
6
|
+
export type DashboardGrouping = 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly';
|
|
1
7
|
/**
|
|
2
8
|
* Filters of the service statistics (`/dashboard`). Every field is optional; the period defaults to
|
|
3
9
|
* the last 7 days.
|
|
@@ -35,6 +41,20 @@ export type DashboardQuery = {
|
|
|
35
41
|
serviceId?: string | string[];
|
|
36
42
|
tagId?: string | string[];
|
|
37
43
|
ticketTopicId?: string | string[];
|
|
44
|
+
/**
|
|
45
|
+
* Granularity of the `getByPeriod` items (the other routes ignore it). Default: `daily`. See
|
|
46
|
+
* {@link DashboardGrouping} for the `name` of the items.
|
|
47
|
+
*/
|
|
48
|
+
grouping?: DashboardGrouping;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* `withTotals: true` makes `getByPeriod`, `getByUser` and `getByDepartment` answer `{ totals, items }`,
|
|
52
|
+
* where `totals` are the `getGeneral` counts of the same filters (`getByTopic` and `getByService` do
|
|
53
|
+
* not take it). Kept out of `DashboardQuery` so a query without it keeps the plain array overload.
|
|
54
|
+
* Only sent to the backend when `true`.
|
|
55
|
+
*/
|
|
56
|
+
export type DashboardWithTotals = {
|
|
57
|
+
withTotals?: boolean;
|
|
38
58
|
};
|
|
39
59
|
/** `GET /dashboard/general` — counts of the period. Times are in seconds. */
|
|
40
60
|
export type DashboardGeneral = {
|
|
@@ -50,4 +70,55 @@ export type DashboardGeneral = {
|
|
|
50
70
|
ticketTime: number;
|
|
51
71
|
contactsCount: number;
|
|
52
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* Counts shared by the grouped routes (`by-period`, `by-user`, `by-department`, `by-topic`,
|
|
75
|
+
* `by-service`). The backend builds each item from the rows it found, so a count is ABSENT (not 0)
|
|
76
|
+
* when the group has no row for it: read them with a default. Times are in seconds.
|
|
77
|
+
*/
|
|
78
|
+
export type DashboardGroupCounts = {
|
|
79
|
+
sentMessagesCount?: number;
|
|
80
|
+
receivedMessagesCount?: number;
|
|
81
|
+
openedTicketsCount?: number;
|
|
82
|
+
closedTicketsCount?: number;
|
|
83
|
+
waitingTime?: number;
|
|
84
|
+
ticketTime?: number;
|
|
85
|
+
contactsCount?: number;
|
|
86
|
+
};
|
|
87
|
+
/** `GET /dashboard/by-period` — the counts of one day of the period (`name` as `dd/MM/yyyy`). */
|
|
88
|
+
export type DashboardPeriodItem = DashboardGroupCounts & {
|
|
89
|
+
name: string;
|
|
90
|
+
totalMessagesCount?: number;
|
|
91
|
+
totalTicketsCount?: number;
|
|
92
|
+
waitingTimeAfterBot?: number;
|
|
93
|
+
waitingTimeAvg?: number;
|
|
94
|
+
};
|
|
95
|
+
/** `GET /dashboard/by-user` — the counts of one user (`name` is the user's name). */
|
|
96
|
+
export type DashboardUserItem = DashboardGroupCounts & {
|
|
97
|
+
name: string;
|
|
98
|
+
ticketTopicsCount?: number;
|
|
99
|
+
};
|
|
100
|
+
/** `GET /dashboard/by-department` — the counts of one department (`name` is its name). */
|
|
101
|
+
export type DashboardDepartmentItem = DashboardGroupCounts & {
|
|
102
|
+
name: string;
|
|
103
|
+
ticketTopicsCount?: number;
|
|
104
|
+
};
|
|
105
|
+
/** `GET /dashboard/by-topic` — the counts of one ticket topic (`name` is the topic's name). */
|
|
106
|
+
export type DashboardTopicItem = DashboardGroupCounts & {
|
|
107
|
+
name: string;
|
|
108
|
+
waitingTimeAfterBot?: number;
|
|
109
|
+
waitingTimeAvg?: number;
|
|
110
|
+
};
|
|
111
|
+
/** `GET /dashboard/by-service` — the counts of one connection (`name` is the connection's name). */
|
|
112
|
+
export type DashboardServiceItem = DashboardGroupCounts & {
|
|
113
|
+
name: string;
|
|
114
|
+
waitingTimeAfterBot?: number;
|
|
115
|
+
waitingTimeAvg?: number;
|
|
116
|
+
ticketTopicsCount?: number;
|
|
117
|
+
};
|
|
118
|
+
/** Answer of `getByPeriod`, `getByUser` and `getByDepartment` with `withTotals: true`. */
|
|
119
|
+
export type DashboardGrouped<TItem> = {
|
|
120
|
+
/** The `getGeneral` counts of the same filters. */
|
|
121
|
+
totals: DashboardGeneral;
|
|
122
|
+
items: TItem[];
|
|
123
|
+
};
|
|
53
124
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/dashboard/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,UAAU,GAAG,WAAW,CAAA;IACrC,6CAA6C;IAC7C,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAChC;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC3C;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;IACrC,UAAU,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,KAAK,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/dashboard/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { expectError, expectType } from 'tsd'
|
|
2
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
3
|
+
import { DashboardApi } from './DashboardApi'
|
|
4
|
+
import type {
|
|
5
|
+
DashboardDepartmentItem,
|
|
6
|
+
DashboardGrouped,
|
|
7
|
+
DashboardPeriodItem,
|
|
8
|
+
DashboardQuery,
|
|
9
|
+
DashboardUserItem,
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
declare const client: ApiClient
|
|
13
|
+
declare const filters: DashboardQuery
|
|
14
|
+
declare const flag: boolean
|
|
15
|
+
const dashboard = new DashboardApi(client)
|
|
16
|
+
|
|
17
|
+
// ── without `withTotals` (or `false`): the plain array ──────────────
|
|
18
|
+
|
|
19
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod())
|
|
20
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod({ grouping: 'weekly' }))
|
|
21
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod({ withTotals: false }))
|
|
22
|
+
expectType<Promise<DashboardPeriodItem[]>>(dashboard.getByPeriod(filters))
|
|
23
|
+
expectType<Promise<DashboardUserItem[]>>(dashboard.getByUser(filters))
|
|
24
|
+
expectType<Promise<DashboardDepartmentItem[]>>(dashboard.getByDepartment(filters))
|
|
25
|
+
|
|
26
|
+
// ── `withTotals: true`: `{ totals, items }` ─────────────────────────
|
|
27
|
+
|
|
28
|
+
expectType<Promise<DashboardGrouped<DashboardPeriodItem>>>(
|
|
29
|
+
dashboard.getByPeriod({ withTotals: true }),
|
|
30
|
+
)
|
|
31
|
+
expectType<Promise<DashboardGrouped<DashboardPeriodItem>>>(
|
|
32
|
+
dashboard.getByPeriod({ ...filters, withTotals: true }),
|
|
33
|
+
)
|
|
34
|
+
expectType<Promise<DashboardGrouped<DashboardUserItem>>>(dashboard.getByUser({ withTotals: true }))
|
|
35
|
+
expectType<Promise<DashboardGrouped<DashboardDepartmentItem>>>(
|
|
36
|
+
dashboard.getByDepartment({ withTotals: true }),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// ── `withTotals` only known at runtime: the union ───────────────────
|
|
40
|
+
|
|
41
|
+
expectType<Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>>>(
|
|
42
|
+
dashboard.getByPeriod({ ...filters, withTotals: flag }),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// ── what the backend does not take ──────────────────────────────────
|
|
46
|
+
|
|
47
|
+
expectError(dashboard.getByPeriod({ grouping: 'quarterly' }))
|
|
48
|
+
expectError(dashboard.getByTopic({ withTotals: true }))
|
|
49
|
+
expectError(dashboard.getByService({ withTotals: true }))
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { DashboardApi } from './DashboardApi'
|
|
3
3
|
import type { ApiClient } from '../../core/ApiClient'
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
DashboardDepartmentItem,
|
|
6
|
+
DashboardGeneral,
|
|
7
|
+
DashboardPeriodItem,
|
|
8
|
+
DashboardQuery,
|
|
9
|
+
DashboardServiceItem,
|
|
10
|
+
DashboardTopicItem,
|
|
11
|
+
DashboardUserItem,
|
|
12
|
+
DashboardWithTotals,
|
|
13
|
+
} from './types'
|
|
5
14
|
|
|
6
15
|
// ── Mock client factory ──────────────────────────────────────────────
|
|
7
16
|
|
|
@@ -33,6 +42,37 @@ const general: DashboardGeneral = {
|
|
|
33
42
|
contactsCount: 4,
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
const periodItems: DashboardPeriodItem[] = [
|
|
46
|
+
{ name: '01/09/2026', sentMessagesCount: 4, receivedMessagesCount: 2, totalMessagesCount: 6 },
|
|
47
|
+
{ name: '02/09/2026', openedTicketsCount: 1, closedTicketsCount: 1, totalTicketsCount: 2 },
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
const userItems: DashboardUserItem[] = [
|
|
51
|
+
{ name: 'Alice', sentMessagesCount: 7, closedTicketsCount: 3, ticketTopicsCount: 1 },
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
const departmentItems: DashboardDepartmentItem[] = [
|
|
55
|
+
{ name: 'Suporte', openedTicketsCount: 2, waitingTime: 60, ticketTime: 600 },
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
const topicItems: DashboardTopicItem[] = [
|
|
59
|
+
{ name: 'Financeiro', openedTicketsCount: 1, waitingTimeAfterBot: 30, waitingTimeAvg: 45 },
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
const serviceItems: DashboardServiceItem[] = [
|
|
63
|
+
{ name: 'WhatsApp', receivedMessagesCount: 5, contactsCount: 4, ticketTopicsCount: 2 },
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
const filters: DashboardQuery = {
|
|
67
|
+
startPeriod: '2026-09-01T00:00:00.000Z',
|
|
68
|
+
endPeriod: '2026-09-07T23:59:59.999Z',
|
|
69
|
+
status: 'closed',
|
|
70
|
+
departmentId: ['d1'],
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const queryString = (query: DashboardQuery & DashboardWithTotals) =>
|
|
74
|
+
`?query=${encodeURIComponent(JSON.stringify(query))}`
|
|
75
|
+
|
|
36
76
|
// ── Tests ────────────────────────────────────────────────────────────
|
|
37
77
|
|
|
38
78
|
describe('DashboardApi', () => {
|
|
@@ -59,15 +99,9 @@ describe('DashboardApi', () => {
|
|
|
59
99
|
|
|
60
100
|
it('sends the filters in the query string', async () => {
|
|
61
101
|
vi.mocked(client.get).mockResolvedValue(general)
|
|
62
|
-
|
|
63
|
-
startPeriod: '2026-09-01T00:00:00.000Z',
|
|
64
|
-
endPeriod: '2026-09-07T23:59:59.999Z',
|
|
65
|
-
status: 'closed' as const,
|
|
66
|
-
departmentId: ['d1'],
|
|
67
|
-
}
|
|
68
|
-
await api.getGeneral(query)
|
|
102
|
+
await api.getGeneral(filters)
|
|
69
103
|
expect(client.get).toHaveBeenCalledWith(
|
|
70
|
-
`/dashboard/general
|
|
104
|
+
`/dashboard/general${queryString(filters)}`,
|
|
71
105
|
undefined,
|
|
72
106
|
)
|
|
73
107
|
})
|
|
@@ -79,4 +113,158 @@ describe('DashboardApi', () => {
|
|
|
79
113
|
expect(client.get).toHaveBeenCalledWith('/dashboard/general', headers)
|
|
80
114
|
})
|
|
81
115
|
})
|
|
116
|
+
|
|
117
|
+
describe('getByPeriod', () => {
|
|
118
|
+
it('calls client.get with /dashboard/by-period and no query string', async () => {
|
|
119
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
120
|
+
const result = await api.getByPeriod()
|
|
121
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-period', undefined)
|
|
122
|
+
expect(result).toEqual(periodItems)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('sends the filters and the grouping in the query string', async () => {
|
|
126
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
127
|
+
const query: DashboardQuery = { ...filters, grouping: 'weekly' }
|
|
128
|
+
await api.getByPeriod(query)
|
|
129
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
130
|
+
`/dashboard/by-period${queryString(query)}`,
|
|
131
|
+
undefined,
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('answers { totals, items } with withTotals: true', async () => {
|
|
136
|
+
const grouped = { totals: general, items: periodItems }
|
|
137
|
+
vi.mocked(client.get).mockResolvedValue(grouped)
|
|
138
|
+
const query = { ...filters, withTotals: true as const }
|
|
139
|
+
const result = await api.getByPeriod(query)
|
|
140
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
141
|
+
`/dashboard/by-period${queryString(query)}`,
|
|
142
|
+
undefined,
|
|
143
|
+
)
|
|
144
|
+
expect(result).toEqual(grouped)
|
|
145
|
+
expect(result.totals.totalTicketsCount).toBe(5)
|
|
146
|
+
expect(result.items).toHaveLength(2)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('does not send withTotals: false (the backend reads it as truthy in the qs format)', async () => {
|
|
150
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
151
|
+
await api.getByPeriod({ ...filters, withTotals: false })
|
|
152
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
153
|
+
`/dashboard/by-period${queryString(filters)}`,
|
|
154
|
+
undefined,
|
|
155
|
+
)
|
|
156
|
+
await api.withQueryFormat('qs').getByPeriod({ withTotals: false, grouping: 'weekly' })
|
|
157
|
+
expect(client.get).toHaveBeenLastCalledWith('/dashboard/by-period?grouping=weekly', undefined)
|
|
158
|
+
await api.getByPeriod({ withTotals: false })
|
|
159
|
+
expect(client.get).toHaveBeenLastCalledWith('/dashboard/by-period', undefined)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('forwards custom headers', async () => {
|
|
163
|
+
vi.mocked(client.get).mockResolvedValue(periodItems)
|
|
164
|
+
const headers = { 'X-Custom': 'h' }
|
|
165
|
+
await api.getByPeriod(undefined, headers)
|
|
166
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-period', headers)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('getByUser', () => {
|
|
171
|
+
it('calls client.get with /dashboard/by-user and the filters', async () => {
|
|
172
|
+
vi.mocked(client.get).mockResolvedValue(userItems)
|
|
173
|
+
const result = await api.getByUser(filters)
|
|
174
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
175
|
+
`/dashboard/by-user${queryString(filters)}`,
|
|
176
|
+
undefined,
|
|
177
|
+
)
|
|
178
|
+
expect(result).toEqual(userItems)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('answers { totals, items } with withTotals: true', async () => {
|
|
182
|
+
const grouped = { totals: general, items: userItems }
|
|
183
|
+
vi.mocked(client.get).mockResolvedValue(grouped)
|
|
184
|
+
const result = await api.getByUser({ withTotals: true })
|
|
185
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
186
|
+
`/dashboard/by-user${queryString({ withTotals: true })}`,
|
|
187
|
+
undefined,
|
|
188
|
+
)
|
|
189
|
+
expect(result.items[0]?.name).toBe('Alice')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('forwards custom headers', async () => {
|
|
193
|
+
vi.mocked(client.get).mockResolvedValue(userItems)
|
|
194
|
+
const headers = { 'X-Custom': 'h' }
|
|
195
|
+
await api.getByUser(undefined, headers)
|
|
196
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-user', headers)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe('getByDepartment', () => {
|
|
201
|
+
it('calls client.get with /dashboard/by-department and the filters', async () => {
|
|
202
|
+
vi.mocked(client.get).mockResolvedValue(departmentItems)
|
|
203
|
+
const result = await api.getByDepartment(filters)
|
|
204
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
205
|
+
`/dashboard/by-department${queryString(filters)}`,
|
|
206
|
+
undefined,
|
|
207
|
+
)
|
|
208
|
+
expect(result).toEqual(departmentItems)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('answers { totals, items } with withTotals: true', async () => {
|
|
212
|
+
const grouped = { totals: general, items: departmentItems }
|
|
213
|
+
vi.mocked(client.get).mockResolvedValue(grouped)
|
|
214
|
+
const result = await api.getByDepartment({ withTotals: true })
|
|
215
|
+
expect(result).toEqual(grouped)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('forwards custom headers', async () => {
|
|
219
|
+
vi.mocked(client.get).mockResolvedValue(departmentItems)
|
|
220
|
+
const headers = { 'X-Custom': 'h' }
|
|
221
|
+
await api.getByDepartment(undefined, headers)
|
|
222
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-department', headers)
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
describe('getByTopic', () => {
|
|
227
|
+
it('calls client.get with /dashboard/by-topic and the filters', async () => {
|
|
228
|
+
vi.mocked(client.get).mockResolvedValue(topicItems)
|
|
229
|
+
const result = await api.getByTopic(filters)
|
|
230
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
231
|
+
`/dashboard/by-topic${queryString(filters)}`,
|
|
232
|
+
undefined,
|
|
233
|
+
)
|
|
234
|
+
expect(result).toEqual(topicItems)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('forwards custom headers', async () => {
|
|
238
|
+
vi.mocked(client.get).mockResolvedValue(topicItems)
|
|
239
|
+
const headers = { 'X-Custom': 'h' }
|
|
240
|
+
await api.getByTopic(undefined, headers)
|
|
241
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-topic', headers)
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
describe('getByService', () => {
|
|
246
|
+
it('calls client.get with /dashboard/by-service and the filters', async () => {
|
|
247
|
+
vi.mocked(client.get).mockResolvedValue(serviceItems)
|
|
248
|
+
const result = await api.getByService(filters)
|
|
249
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
250
|
+
`/dashboard/by-service${queryString(filters)}`,
|
|
251
|
+
undefined,
|
|
252
|
+
)
|
|
253
|
+
expect(result).toEqual(serviceItems)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('forwards custom headers', async () => {
|
|
257
|
+
vi.mocked(client.get).mockResolvedValue(serviceItems)
|
|
258
|
+
const headers = { 'X-Custom': 'h' }
|
|
259
|
+
await api.getByService(undefined, headers)
|
|
260
|
+
expect(client.get).toHaveBeenCalledWith('/dashboard/by-service', headers)
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
describe('error propagation', () => {
|
|
265
|
+
it('propagates errors from the grouped routes', async () => {
|
|
266
|
+
vi.mocked(client.get).mockRejectedValue(new Error('Unauthorized'))
|
|
267
|
+
await expect(api.getByPeriod()).rejects.toThrow('Unauthorized')
|
|
268
|
+
})
|
|
269
|
+
})
|
|
82
270
|
})
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { BaseApi } from '../../core/BaseApi'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
DashboardDepartmentItem,
|
|
4
|
+
DashboardGeneral,
|
|
5
|
+
DashboardGrouped,
|
|
6
|
+
DashboardPeriodItem,
|
|
7
|
+
DashboardQuery,
|
|
8
|
+
DashboardServiceItem,
|
|
9
|
+
DashboardTopicItem,
|
|
10
|
+
DashboardUserItem,
|
|
11
|
+
DashboardWithTotals,
|
|
12
|
+
} from './types'
|
|
3
13
|
|
|
4
14
|
/**
|
|
5
15
|
* Service statistics ("Estatísticas de atendimento"). The `/dashboard` routes only require an
|
|
6
|
-
* authenticated user (no permission check).
|
|
16
|
+
* authenticated user (no permission check). Every route takes the same {@link DashboardQuery}
|
|
17
|
+
* filters; the grouped routes answer one item per day, user, department, topic or connection.
|
|
7
18
|
*/
|
|
8
19
|
export class DashboardApi extends BaseApi {
|
|
9
20
|
public readonly basePath = '/dashboard'
|
|
@@ -15,4 +26,121 @@ export class DashboardApi extends BaseApi {
|
|
|
15
26
|
headers,
|
|
16
27
|
)
|
|
17
28
|
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The counts of each bucket of the period (`GET /dashboard/by-period`): one item per day by default,
|
|
32
|
+
* or per `grouping` (hour, week, month, year). With `withTotals: true` the answer is
|
|
33
|
+
* `{ totals, items }`.
|
|
34
|
+
*/
|
|
35
|
+
getByPeriod(
|
|
36
|
+
query: DashboardQuery & { withTotals: true },
|
|
37
|
+
headers?: Record<string, string>,
|
|
38
|
+
): Promise<DashboardGrouped<DashboardPeriodItem>>
|
|
39
|
+
getByPeriod(
|
|
40
|
+
query?: DashboardQuery & { withTotals?: false },
|
|
41
|
+
headers?: Record<string, string>,
|
|
42
|
+
): Promise<DashboardPeriodItem[]>
|
|
43
|
+
/** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */
|
|
44
|
+
getByPeriod(
|
|
45
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
46
|
+
headers?: Record<string, string>,
|
|
47
|
+
): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>>
|
|
48
|
+
getByPeriod(
|
|
49
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
50
|
+
headers?: Record<string, string>,
|
|
51
|
+
): Promise<DashboardPeriodItem[] | DashboardGrouped<DashboardPeriodItem>> {
|
|
52
|
+
return this.getGrouped<DashboardPeriodItem>('by-period', query, headers)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The counts of each user of the period (`GET /dashboard/by-user`). With `withTotals: true` the
|
|
57
|
+
* answer is `{ totals, items }`.
|
|
58
|
+
*/
|
|
59
|
+
getByUser(
|
|
60
|
+
query: DashboardQuery & { withTotals: true },
|
|
61
|
+
headers?: Record<string, string>,
|
|
62
|
+
): Promise<DashboardGrouped<DashboardUserItem>>
|
|
63
|
+
getByUser(
|
|
64
|
+
query?: DashboardQuery & { withTotals?: false },
|
|
65
|
+
headers?: Record<string, string>,
|
|
66
|
+
): Promise<DashboardUserItem[]>
|
|
67
|
+
/** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */
|
|
68
|
+
getByUser(
|
|
69
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
70
|
+
headers?: Record<string, string>,
|
|
71
|
+
): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>>
|
|
72
|
+
getByUser(
|
|
73
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
74
|
+
headers?: Record<string, string>,
|
|
75
|
+
): Promise<DashboardUserItem[] | DashboardGrouped<DashboardUserItem>> {
|
|
76
|
+
return this.getGrouped<DashboardUserItem>('by-user', query, headers)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The counts of each department of the period (`GET /dashboard/by-department`). With
|
|
81
|
+
* `withTotals: true` the answer is `{ totals, items }`.
|
|
82
|
+
*/
|
|
83
|
+
getByDepartment(
|
|
84
|
+
query: DashboardQuery & { withTotals: true },
|
|
85
|
+
headers?: Record<string, string>,
|
|
86
|
+
): Promise<DashboardGrouped<DashboardDepartmentItem>>
|
|
87
|
+
getByDepartment(
|
|
88
|
+
query?: DashboardQuery & { withTotals?: false },
|
|
89
|
+
headers?: Record<string, string>,
|
|
90
|
+
): Promise<DashboardDepartmentItem[]>
|
|
91
|
+
/** When `withTotals` is only known at runtime (e.g. a `boolean` variable). */
|
|
92
|
+
getByDepartment(
|
|
93
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
94
|
+
headers?: Record<string, string>,
|
|
95
|
+
): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>>
|
|
96
|
+
getByDepartment(
|
|
97
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
98
|
+
headers?: Record<string, string>,
|
|
99
|
+
): Promise<DashboardDepartmentItem[] | DashboardGrouped<DashboardDepartmentItem>> {
|
|
100
|
+
return this.getGrouped<DashboardDepartmentItem>('by-department', query, headers)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The counts of each ticket topic of the period (`GET /dashboard/by-topic`). Does not take
|
|
105
|
+
* `withTotals` (the backend ignores it).
|
|
106
|
+
*/
|
|
107
|
+
getByTopic(
|
|
108
|
+
query?: DashboardQuery,
|
|
109
|
+
headers?: Record<string, string>,
|
|
110
|
+
): Promise<DashboardTopicItem[]> {
|
|
111
|
+
return this.client.get<DashboardTopicItem[]>(
|
|
112
|
+
`${this.basePath}/by-topic${this.toQueryString(query)}`,
|
|
113
|
+
headers,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The counts of each connection of the period (`GET /dashboard/by-service`). Does not take
|
|
119
|
+
* `withTotals` (the backend ignores it).
|
|
120
|
+
*/
|
|
121
|
+
getByService(
|
|
122
|
+
query?: DashboardQuery,
|
|
123
|
+
headers?: Record<string, string>,
|
|
124
|
+
): Promise<DashboardServiceItem[]> {
|
|
125
|
+
return this.client.get<DashboardServiceItem[]>(
|
|
126
|
+
`${this.basePath}/by-service${this.toQueryString(query)}`,
|
|
127
|
+
headers,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* `withTotals` is only sent when `true`: the backend reads it with a truthiness check, so in the
|
|
133
|
+
* `qs` format a serialized `withTotals=false` (the string `'false'`) would wrap the answer anyway.
|
|
134
|
+
*/
|
|
135
|
+
private getGrouped<TItem>(
|
|
136
|
+
route: string,
|
|
137
|
+
query?: DashboardQuery & DashboardWithTotals,
|
|
138
|
+
headers?: Record<string, string>,
|
|
139
|
+
): Promise<TItem[] | DashboardGrouped<TItem>> {
|
|
140
|
+
const { withTotals, ...filters } = query ?? {}
|
|
141
|
+
return this.client.get<TItem[] | DashboardGrouped<TItem>>(
|
|
142
|
+
`${this.basePath}/${route}${this.toQueryString(withTotals ? { ...filters, withTotals: true } : filters)}`,
|
|
143
|
+
headers,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
18
146
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Granularity of the by-period items (`grouping`). Default: `daily`. Whatever the granularity, the
|
|
3
|
+
* `name` of each item is its day (`dd/MM/yyyy`), so with `hourly` the items of one day share the same
|
|
4
|
+
* `name` (they come in chronological order).
|
|
5
|
+
*/
|
|
6
|
+
export type DashboardGrouping = 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly'
|
|
7
|
+
|
|
1
8
|
/**
|
|
2
9
|
* Filters of the service statistics (`/dashboard`). Every field is optional; the period defaults to
|
|
3
10
|
* the last 7 days.
|
|
@@ -35,8 +42,21 @@ export type DashboardQuery = {
|
|
|
35
42
|
serviceId?: string | string[]
|
|
36
43
|
tagId?: string | string[]
|
|
37
44
|
ticketTopicId?: string | string[]
|
|
45
|
+
/**
|
|
46
|
+
* Granularity of the `getByPeriod` items (the other routes ignore it). Default: `daily`. See
|
|
47
|
+
* {@link DashboardGrouping} for the `name` of the items.
|
|
48
|
+
*/
|
|
49
|
+
grouping?: DashboardGrouping
|
|
38
50
|
}
|
|
39
51
|
|
|
52
|
+
/**
|
|
53
|
+
* `withTotals: true` makes `getByPeriod`, `getByUser` and `getByDepartment` answer `{ totals, items }`,
|
|
54
|
+
* where `totals` are the `getGeneral` counts of the same filters (`getByTopic` and `getByService` do
|
|
55
|
+
* not take it). Kept out of `DashboardQuery` so a query without it keeps the plain array overload.
|
|
56
|
+
* Only sent to the backend when `true`.
|
|
57
|
+
*/
|
|
58
|
+
export type DashboardWithTotals = { withTotals?: boolean }
|
|
59
|
+
|
|
40
60
|
/** `GET /dashboard/general` — counts of the period. Times are in seconds. */
|
|
41
61
|
export type DashboardGeneral = {
|
|
42
62
|
sentMessagesCount: number
|
|
@@ -51,3 +71,61 @@ export type DashboardGeneral = {
|
|
|
51
71
|
ticketTime: number
|
|
52
72
|
contactsCount: number
|
|
53
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Counts shared by the grouped routes (`by-period`, `by-user`, `by-department`, `by-topic`,
|
|
77
|
+
* `by-service`). The backend builds each item from the rows it found, so a count is ABSENT (not 0)
|
|
78
|
+
* when the group has no row for it: read them with a default. Times are in seconds.
|
|
79
|
+
*/
|
|
80
|
+
export type DashboardGroupCounts = {
|
|
81
|
+
sentMessagesCount?: number
|
|
82
|
+
receivedMessagesCount?: number
|
|
83
|
+
openedTicketsCount?: number
|
|
84
|
+
closedTicketsCount?: number
|
|
85
|
+
waitingTime?: number
|
|
86
|
+
ticketTime?: number
|
|
87
|
+
contactsCount?: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** `GET /dashboard/by-period` — the counts of one day of the period (`name` as `dd/MM/yyyy`). */
|
|
91
|
+
export type DashboardPeriodItem = DashboardGroupCounts & {
|
|
92
|
+
name: string
|
|
93
|
+
totalMessagesCount?: number
|
|
94
|
+
totalTicketsCount?: number
|
|
95
|
+
waitingTimeAfterBot?: number
|
|
96
|
+
waitingTimeAvg?: number
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** `GET /dashboard/by-user` — the counts of one user (`name` is the user's name). */
|
|
100
|
+
export type DashboardUserItem = DashboardGroupCounts & {
|
|
101
|
+
name: string
|
|
102
|
+
ticketTopicsCount?: number
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** `GET /dashboard/by-department` — the counts of one department (`name` is its name). */
|
|
106
|
+
export type DashboardDepartmentItem = DashboardGroupCounts & {
|
|
107
|
+
name: string
|
|
108
|
+
ticketTopicsCount?: number
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** `GET /dashboard/by-topic` — the counts of one ticket topic (`name` is the topic's name). */
|
|
112
|
+
export type DashboardTopicItem = DashboardGroupCounts & {
|
|
113
|
+
name: string
|
|
114
|
+
waitingTimeAfterBot?: number
|
|
115
|
+
waitingTimeAvg?: number
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** `GET /dashboard/by-service` — the counts of one connection (`name` is the connection's name). */
|
|
119
|
+
export type DashboardServiceItem = DashboardGroupCounts & {
|
|
120
|
+
name: string
|
|
121
|
+
waitingTimeAfterBot?: number
|
|
122
|
+
waitingTimeAvg?: number
|
|
123
|
+
ticketTopicsCount?: number
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Answer of `getByPeriod`, `getByUser` and `getByDepartment` with `withTotals: true`. */
|
|
127
|
+
export type DashboardGrouped<TItem> = {
|
|
128
|
+
/** The `getGeneral` counts of the same filters. */
|
|
129
|
+
totals: DashboardGeneral
|
|
130
|
+
items: TItem[]
|
|
131
|
+
}
|
|
@@ -99,6 +99,70 @@ const cases: Case[] = [
|
|
|
99
99
|
})
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: 'DashboardApi.getByPeriod',
|
|
104
|
+
path: '/dashboard/by-period',
|
|
105
|
+
query: { status: 'closed', departmentId: ['d1'], grouping: 'weekly', withTotals: true },
|
|
106
|
+
call: (client, f) => {
|
|
107
|
+
const api = new DashboardApi(client)
|
|
108
|
+
return (f ? api.withQueryFormat(f) : api).getByPeriod({
|
|
109
|
+
status: 'closed',
|
|
110
|
+
departmentId: ['d1'],
|
|
111
|
+
grouping: 'weekly',
|
|
112
|
+
withTotals: true,
|
|
113
|
+
})
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'DashboardApi.getByUser',
|
|
118
|
+
path: '/dashboard/by-user',
|
|
119
|
+
query: { status: 'closed', departmentId: ['d1'], withTotals: true },
|
|
120
|
+
call: (client, f) => {
|
|
121
|
+
const api = new DashboardApi(client)
|
|
122
|
+
return (f ? api.withQueryFormat(f) : api).getByUser({
|
|
123
|
+
status: 'closed',
|
|
124
|
+
departmentId: ['d1'],
|
|
125
|
+
withTotals: true,
|
|
126
|
+
})
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'DashboardApi.getByDepartment',
|
|
131
|
+
path: '/dashboard/by-department',
|
|
132
|
+
query: { status: 'closed', departmentId: ['d1'], withTotals: true },
|
|
133
|
+
call: (client, f) => {
|
|
134
|
+
const api = new DashboardApi(client)
|
|
135
|
+
return (f ? api.withQueryFormat(f) : api).getByDepartment({
|
|
136
|
+
status: 'closed',
|
|
137
|
+
departmentId: ['d1'],
|
|
138
|
+
withTotals: true,
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'DashboardApi.getByTopic',
|
|
144
|
+
path: '/dashboard/by-topic',
|
|
145
|
+
query: { status: 'closed', departmentId: ['d1'] },
|
|
146
|
+
call: (client, f) => {
|
|
147
|
+
const api = new DashboardApi(client)
|
|
148
|
+
return (f ? api.withQueryFormat(f) : api).getByTopic({
|
|
149
|
+
status: 'closed',
|
|
150
|
+
departmentId: ['d1'],
|
|
151
|
+
})
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'DashboardApi.getByService',
|
|
156
|
+
path: '/dashboard/by-service',
|
|
157
|
+
query: { status: 'closed', departmentId: ['d1'] },
|
|
158
|
+
call: (client, f) => {
|
|
159
|
+
const api = new DashboardApi(client)
|
|
160
|
+
return (f ? api.withQueryFormat(f) : api).getByService({
|
|
161
|
+
status: 'closed',
|
|
162
|
+
departmentId: ['d1'],
|
|
163
|
+
})
|
|
164
|
+
},
|
|
165
|
+
},
|
|
102
166
|
{
|
|
103
167
|
name: 'NowApi.getResume',
|
|
104
168
|
path: '/now/resume',
|