@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.
@@ -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 { DashboardGeneral, DashboardQuery } from './types'\n\n/**\n * Service statistics (\"Estatísticas de atendimento\"). The `/dashboard` routes only require an\n * authenticated user (no permission check).\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"],"mappings":";;;;;;;;AAOA,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"}
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,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE/D;;;GAGG;AACH,qBAAa,YAAa,SAAQ,OAAO;IACvC,SAAgB,QAAQ,gBAAe;IAEvC,yFAAyF;IACzF,UAAU,CAAC,KAAK,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAMhG"}
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,EACV,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,SAAS,CAAA;AAEhB;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,OAAO;IACvC,SAAgB,QAAQ,gBAAe;IAEvC,yFAAyF;IACzF,UAAU,CAAC,KAAK,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAO/F;;;;OAIG;IACH,WAAW,CACT,KAAK,EAAE,cAAc,GAAG;QAAE,UAAU,EAAE,IAAI,CAAA;KAAE,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;IACjD,WAAW,CACT,KAAK,CAAC,EAAE,cAAc,GAAG;QAAE,UAAU,CAAC,EAAE,KAAK,CAAA;KAAE,EAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,mBAAmB,EAAE,CAAC;IACjC,8EAA8E;IAC9E,WAAW,CACT,KAAK,CAAC,EAAE,cAAc,GAAG,mBAAmB,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,mBAAmB,EAAE,GAAG,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;IAQzE;;;OAGG;IACH,SAAS,CACP,KAAK,EAAE,cAAc,GAAG;QAAE,UAAU,EAAE,IAAI,CAAA;KAAE,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAC/C,SAAS,CACP,KAAK,CAAC,EAAE,cAAc,GAAG;QAAE,UAAU,CAAC,EAAE,KAAK,CAAA;KAAE,EAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAC/B,8EAA8E;IAC9E,SAAS,CACP,KAAK,CAAC,EAAE,cAAc,GAAG,mBAAmB,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,iBAAiB,EAAE,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAQrE;;;OAGG;IACH,eAAe,CACb,KAAK,EAAE,cAAc,GAAG;QAAE,UAAU,EAAE,IAAI,CAAA;KAAE,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IACrD,eAAe,CACb,KAAK,CAAC,EAAE,cAAc,GAAG;QAAE,UAAU,CAAC,EAAE,KAAK,CAAA;KAAE,EAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,uBAAuB,EAAE,CAAC;IACrC,8EAA8E;IAC9E,eAAe,CACb,KAAK,CAAC,EAAE,cAAc,GAAG,mBAAmB,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,uBAAuB,EAAE,GAAG,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IAQjF;;;OAGG;IACH,UAAU,CACR,KAAK,CAAC,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAOhC;;;OAGG;IACH,YAAY,CACV,KAAK,CAAC,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAOlC;;;OAGG;IACH,OAAO,CAAC,UAAU;CAWnB"}
@@ -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 { DashboardGeneral, DashboardQuery } from './types'\n\n/**\n * Service statistics (\"Estatísticas de atendimento\"). The `/dashboard` routes only require an\n * authenticated user (no permission check).\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"],"mappings":";;;;;;;AAOA,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"}
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;CAClC,CAAA;AAED,6EAA6E;AAC7E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/apis/dashboard/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEpF;;;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;IACjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAA;CAC7B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE1D,6EAA6E;AAC7E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iBAAiB,EAAE,MAAM,CAAA;IACzB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,iBAAiB,EAAE,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,iGAAiG;AACjG,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,GAAG;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,qFAAqF;AACrF,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,GAAG;IACrD,IAAI,EAAE,MAAM,CAAA;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,0FAA0F;AAC1F,MAAM,MAAM,uBAAuB,GAAG,oBAAoB,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAA;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,+FAA+F;AAC/F,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,GAAG;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,GAAG;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,CAAC,KAAK,IAAI;IACpC,mDAAmD;IACnD,MAAM,EAAE,gBAAgB,CAAA;IACxB,KAAK,EAAE,KAAK,EAAE,CAAA;CACf,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikatec/digisac-api-sdk",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -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 { DashboardGeneral } from './types'
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
- const query = {
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?query=${encodeURIComponent(JSON.stringify(query))}`,
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 { DashboardGeneral, DashboardQuery } from './types'
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',