@ikatec/digisac-api-sdk 4.0.0 → 4.2.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.
Files changed (224) hide show
  1. package/README.md +34 -4
  2. package/dist/apis/authHistory/AuthHistoryApi.cjs +11 -17
  3. package/dist/apis/authHistory/AuthHistoryApi.cjs.map +1 -1
  4. package/dist/apis/authHistory/AuthHistoryApi.d.ts +10 -18
  5. package/dist/apis/authHistory/AuthHistoryApi.d.ts.map +1 -1
  6. package/dist/apis/authHistory/AuthHistoryApi.mjs +11 -15
  7. package/dist/apis/authHistory/AuthHistoryApi.mjs.map +1 -1
  8. package/dist/apis/authHistory/types.d.ts +4 -3
  9. package/dist/apis/authHistory/types.d.ts.map +1 -1
  10. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs +2 -5
  11. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs.map +1 -1
  12. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts +2 -4
  13. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts.map +1 -1
  14. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs +3 -5
  15. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs.map +1 -1
  16. package/dist/apis/contacts/ContactsApi.cjs +2 -2
  17. package/dist/apis/contacts/ContactsApi.cjs.map +1 -1
  18. package/dist/apis/contacts/ContactsApi.mjs +2 -2
  19. package/dist/apis/contacts/ContactsApi.mjs.map +1 -1
  20. package/dist/apis/customFields/types.d.ts +21 -19
  21. package/dist/apis/customFields/types.d.ts.map +1 -1
  22. package/dist/apis/dashboard/DashboardApi.cjs +19 -0
  23. package/dist/apis/dashboard/DashboardApi.cjs.map +1 -0
  24. package/dist/apis/dashboard/DashboardApi.d.ts +12 -0
  25. package/dist/apis/dashboard/DashboardApi.d.ts.map +1 -0
  26. package/dist/apis/dashboard/DashboardApi.mjs +18 -0
  27. package/dist/apis/dashboard/DashboardApi.mjs.map +1 -0
  28. package/dist/apis/dashboard/index.cjs +4 -0
  29. package/dist/apis/dashboard/index.d.ts +3 -0
  30. package/dist/apis/dashboard/index.d.ts.map +1 -0
  31. package/dist/apis/dashboard/index.mjs +3 -0
  32. package/dist/apis/dashboard/types.cjs +0 -0
  33. package/dist/apis/dashboard/types.d.ts +53 -0
  34. package/dist/apis/dashboard/types.d.ts.map +1 -0
  35. package/dist/apis/dashboard/types.mjs +0 -0
  36. package/dist/apis/holiday/types.d.ts +34 -8
  37. package/dist/apis/holiday/types.d.ts.map +1 -1
  38. package/dist/apis/index.cjs +6 -0
  39. package/dist/apis/index.d.ts +2 -0
  40. package/dist/apis/index.d.ts.map +1 -1
  41. package/dist/apis/index.mjs +5 -1
  42. package/dist/apis/me/MeApi.cjs +2 -5
  43. package/dist/apis/me/MeApi.cjs.map +1 -1
  44. package/dist/apis/me/MeApi.d.ts +2 -4
  45. package/dist/apis/me/MeApi.d.ts.map +1 -1
  46. package/dist/apis/me/MeApi.mjs +3 -5
  47. package/dist/apis/me/MeApi.mjs.map +1 -1
  48. package/dist/apis/now/NowApi.cjs +37 -0
  49. package/dist/apis/now/NowApi.cjs.map +1 -0
  50. package/dist/apis/now/NowApi.d.ts +26 -0
  51. package/dist/apis/now/NowApi.d.ts.map +1 -0
  52. package/dist/apis/now/NowApi.mjs +36 -0
  53. package/dist/apis/now/NowApi.mjs.map +1 -0
  54. package/dist/apis/now/index.cjs +4 -0
  55. package/dist/apis/now/index.d.ts +3 -0
  56. package/dist/apis/now/index.d.ts.map +1 -0
  57. package/dist/apis/now/index.mjs +3 -0
  58. package/dist/apis/now/types.cjs +0 -0
  59. package/dist/apis/now/types.d.ts +45 -0
  60. package/dist/apis/now/types.d.ts.map +1 -0
  61. package/dist/apis/now/types.mjs +0 -0
  62. package/dist/apis/people/types.d.ts +7 -2
  63. package/dist/apis/people/types.d.ts.map +1 -1
  64. package/dist/apis/permissions/PermissionsApi.cjs +6 -2
  65. package/dist/apis/permissions/PermissionsApi.cjs.map +1 -1
  66. package/dist/apis/permissions/PermissionsApi.d.ts +7 -3
  67. package/dist/apis/permissions/PermissionsApi.d.ts.map +1 -1
  68. package/dist/apis/permissions/PermissionsApi.mjs +6 -2
  69. package/dist/apis/permissions/PermissionsApi.mjs.map +1 -1
  70. package/dist/apis/permissions/types.d.ts +4 -14
  71. package/dist/apis/permissions/types.d.ts.map +1 -1
  72. package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.cjs +5 -0
  73. package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.cjs.map +1 -1
  74. package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.d.ts +5 -0
  75. package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.d.ts.map +1 -1
  76. package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.mjs +5 -0
  77. package/dist/apis/personalAccessTokens/PersonalAccessTokensApi.mjs.map +1 -1
  78. package/dist/apis/personalAccessTokens/types.d.ts +23 -4
  79. package/dist/apis/personalAccessTokens/types.d.ts.map +1 -1
  80. package/dist/apis/roles/RolesApi.cjs.map +1 -1
  81. package/dist/apis/roles/RolesApi.d.ts +2 -2
  82. package/dist/apis/roles/RolesApi.d.ts.map +1 -1
  83. package/dist/apis/roles/RolesApi.mjs.map +1 -1
  84. package/dist/apis/roles/types.d.ts +27 -4
  85. package/dist/apis/roles/types.d.ts.map +1 -1
  86. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs +3 -9
  87. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs.map +1 -1
  88. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts +3 -4
  89. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts.map +1 -1
  90. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs +3 -7
  91. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs.map +1 -1
  92. package/dist/apis/stickerUsers/StickerUsersApi.cjs +3 -9
  93. package/dist/apis/stickerUsers/StickerUsersApi.cjs.map +1 -1
  94. package/dist/apis/stickerUsers/StickerUsersApi.d.ts +3 -4
  95. package/dist/apis/stickerUsers/StickerUsersApi.d.ts.map +1 -1
  96. package/dist/apis/stickerUsers/StickerUsersApi.mjs +3 -7
  97. package/dist/apis/stickerUsers/StickerUsersApi.mjs.map +1 -1
  98. package/dist/apis/terms/TermsApi.cjs +7 -13
  99. package/dist/apis/terms/TermsApi.cjs.map +1 -1
  100. package/dist/apis/terms/TermsApi.d.ts +6 -5
  101. package/dist/apis/terms/TermsApi.d.ts.map +1 -1
  102. package/dist/apis/terms/TermsApi.mjs +7 -11
  103. package/dist/apis/terms/TermsApi.mjs.map +1 -1
  104. package/dist/apis/timetable/types.d.ts +12 -7
  105. package/dist/apis/timetable/types.d.ts.map +1 -1
  106. package/dist/apis/transcripts/TranscriptsApi.cjs +2 -5
  107. package/dist/apis/transcripts/TranscriptsApi.cjs.map +1 -1
  108. package/dist/apis/transcripts/TranscriptsApi.d.ts +2 -4
  109. package/dist/apis/transcripts/TranscriptsApi.d.ts.map +1 -1
  110. package/dist/apis/transcripts/TranscriptsApi.mjs +3 -5
  111. package/dist/apis/transcripts/TranscriptsApi.mjs.map +1 -1
  112. package/dist/core/ApiClient.d.ts +3 -0
  113. package/dist/core/ApiClient.d.ts.map +1 -1
  114. package/dist/core/ArchivableCrudApi.cjs +0 -2
  115. package/dist/core/ArchivableCrudApi.cjs.map +1 -1
  116. package/dist/core/ArchivableCrudApi.d.ts.map +1 -1
  117. package/dist/core/ArchivableCrudApi.mjs +0 -2
  118. package/dist/core/ArchivableCrudApi.mjs.map +1 -1
  119. package/dist/core/BaseApi.cjs +34 -0
  120. package/dist/core/BaseApi.cjs.map +1 -0
  121. package/dist/core/BaseApi.d.ts +20 -0
  122. package/dist/core/BaseApi.d.ts.map +1 -0
  123. package/dist/core/BaseApi.mjs +33 -0
  124. package/dist/core/BaseApi.mjs.map +1 -0
  125. package/dist/core/BaseApiClient.cjs +4 -1
  126. package/dist/core/BaseApiClient.cjs.map +1 -1
  127. package/dist/core/BaseApiClient.d.ts +7 -1
  128. package/dist/core/BaseApiClient.d.ts.map +1 -1
  129. package/dist/core/BaseApiClient.mjs +4 -1
  130. package/dist/core/BaseApiClient.mjs.map +1 -1
  131. package/dist/core/BaseCrudApi.cjs +3 -40
  132. package/dist/core/BaseCrudApi.cjs.map +1 -1
  133. package/dist/core/BaseCrudApi.d.ts +4 -23
  134. package/dist/core/BaseCrudApi.d.ts.map +1 -1
  135. package/dist/core/BaseCrudApi.mjs +3 -38
  136. package/dist/core/BaseCrudApi.mjs.map +1 -1
  137. package/dist/core/BaseReadApi.cjs +44 -0
  138. package/dist/core/BaseReadApi.cjs.map +1 -0
  139. package/dist/core/BaseReadApi.d.ts +32 -0
  140. package/dist/core/BaseReadApi.d.ts.map +1 -0
  141. package/dist/core/BaseReadApi.mjs +43 -0
  142. package/dist/core/BaseReadApi.mjs.map +1 -0
  143. package/dist/core/bindMethods.cjs +24 -0
  144. package/dist/core/bindMethods.cjs.map +1 -0
  145. package/dist/core/bindMethods.d.ts +7 -0
  146. package/dist/core/bindMethods.d.ts.map +1 -0
  147. package/dist/core/bindMethods.mjs +22 -0
  148. package/dist/core/bindMethods.mjs.map +1 -0
  149. package/dist/core/index.cjs +4 -0
  150. package/dist/core/index.d.ts +4 -0
  151. package/dist/core/index.d.ts.map +1 -1
  152. package/dist/core/index.mjs +3 -1
  153. package/dist/core/queryString.cjs +22 -0
  154. package/dist/core/queryString.cjs.map +1 -0
  155. package/dist/core/queryString.d.ts +14 -0
  156. package/dist/core/queryString.d.ts.map +1 -0
  157. package/dist/core/queryString.mjs +18 -0
  158. package/dist/core/queryString.mjs.map +1 -0
  159. package/dist/core/types.d.ts +2 -0
  160. package/dist/core/types.d.ts.map +1 -1
  161. package/dist/core/withQueryFormat.cjs +42 -0
  162. package/dist/core/withQueryFormat.cjs.map +1 -0
  163. package/dist/core/withQueryFormat.d.ts +10 -0
  164. package/dist/core/withQueryFormat.d.ts.map +1 -0
  165. package/dist/core/withQueryFormat.mjs +40 -0
  166. package/dist/core/withQueryFormat.mjs.map +1 -0
  167. package/dist/index.cjs +4 -0
  168. package/dist/index.mjs +3 -1
  169. package/package.json +1 -1
  170. package/src/apis/authHistory/AuthHistoryApi.test.ts +11 -3
  171. package/src/apis/authHistory/AuthHistoryApi.ts +11 -37
  172. package/src/apis/authHistory/types.ts +4 -3
  173. package/src/apis/campaigns/CampaignsApi.test.ts +1 -1
  174. package/src/apis/clientFeedback/ClientFeedbackApi.ts +5 -7
  175. package/src/apis/contacts/ContactsApi.test.ts +1 -1
  176. package/src/apis/contacts/ContactsApi.ts +2 -2
  177. package/src/apis/customFields/types.ts +21 -25
  178. package/src/apis/dashboard/DashboardApi.test.ts +82 -0
  179. package/src/apis/dashboard/DashboardApi.ts +18 -0
  180. package/src/apis/dashboard/index.ts +2 -0
  181. package/src/apis/dashboard/types.ts +53 -0
  182. package/src/apis/holiday/types.ts +36 -8
  183. package/src/apis/index.ts +2 -0
  184. package/src/apis/me/MeApi.ts +2 -8
  185. package/src/apis/messages/MessagesApi.test.ts +2 -2
  186. package/src/apis/now/NowApi.test.ts +125 -0
  187. package/src/apis/now/NowApi.ts +59 -0
  188. package/src/apis/now/index.ts +2 -0
  189. package/src/apis/now/types.ts +47 -0
  190. package/src/apis/payloads.test-d.ts +39 -0
  191. package/src/apis/payloads.test.ts +81 -0
  192. package/src/apis/people/types.ts +7 -2
  193. package/src/apis/permissions/PermissionsApi.test.ts +93 -0
  194. package/src/apis/permissions/PermissionsApi.ts +7 -7
  195. package/src/apis/permissions/types.ts +4 -16
  196. package/src/apis/personalAccessTokens/PersonalAccessTokensApi.ts +5 -0
  197. package/src/apis/personalAccessTokens/types.ts +25 -4
  198. package/src/apis/queryFormat.test.ts +273 -0
  199. package/src/apis/roles/RolesApi.ts +7 -2
  200. package/src/apis/roles/types.ts +27 -4
  201. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.test.ts +2 -2
  202. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.ts +10 -10
  203. package/src/apis/stickerUsers/StickerUsersApi.test.ts +1 -1
  204. package/src/apis/stickerUsers/StickerUsersApi.ts +7 -10
  205. package/src/apis/terms/TermsApi.ts +7 -13
  206. package/src/apis/timetable/types.ts +13 -7
  207. package/src/apis/transcripts/TranscriptsApi.ts +2 -7
  208. package/src/core/ApiClient.ts +4 -0
  209. package/src/core/ArchivableCrudApi.ts +0 -2
  210. package/src/core/BaseApi.test.ts +164 -0
  211. package/src/core/BaseApi.ts +32 -0
  212. package/src/core/BaseApiClient.test.ts +10 -0
  213. package/src/core/BaseApiClient.ts +9 -1
  214. package/src/core/BaseCrudApi.test-d.ts +9 -0
  215. package/src/core/BaseCrudApi.test.ts +131 -60
  216. package/src/core/BaseCrudApi.ts +7 -63
  217. package/src/core/BaseReadApi.ts +70 -0
  218. package/src/core/bindMethods.ts +26 -0
  219. package/src/core/index.ts +4 -0
  220. package/src/core/queryString.test.ts +71 -0
  221. package/src/core/queryString.ts +30 -0
  222. package/src/core/types.ts +5 -0
  223. package/src/core/withQueryFormat.test.ts +136 -0
  224. package/src/core/withQueryFormat.ts +47 -0
@@ -1,43 +1,17 @@
1
- import qs from 'qs'
2
1
  import type { ApiClient } from '../../core/ApiClient'
3
- import type { ListQuery, Paginated, HasGetMany, HasGetById } from '../../core/types'
2
+ import { BaseReadApi } from '../../core/BaseReadApi'
4
3
  import type { AuthHistory } from './types'
5
4
 
6
- export class AuthHistoryApi implements HasGetMany<AuthHistory>, HasGetById<AuthHistory> {
7
- private readonly client: ApiClient
8
-
5
+ /**
6
+ * Authentication events (auth, password_change, logout), read-only (guarded by
7
+ * `authHistory.view`). Non-admin users can only see and access their own records.
8
+ *
9
+ * Since 4.2 `getById` has the base signature `(id, query?, headers?)`: a call written as
10
+ * `getById(id, headers)` with the headers in a variable still compiles, but the headers are sent
11
+ * as the query string — move them to the third argument.
12
+ */
13
+ export class AuthHistoryApi extends BaseReadApi<AuthHistory> {
9
14
  constructor(client: ApiClient) {
10
- this.client = client
11
- }
12
-
13
- /**
14
- * Returns a paginated list of authentication events (auth, password_change, logout). Non-admin users can only see their own records.
15
- * @permissions authHistory.view
16
- */
17
- getMany(
18
- query: ListQuery<AuthHistory> & { paginate: false },
19
- headers?: Record<string, string>,
20
- ): Promise<AuthHistory[]>
21
- getMany(
22
- query?: ListQuery<AuthHistory> & { paginate?: true },
23
- headers?: Record<string, string>,
24
- ): Promise<Paginated<AuthHistory>>
25
- getMany(
26
- query?: ListQuery<AuthHistory>,
27
- headers?: Record<string, string>,
28
- ): Promise<Paginated<AuthHistory> | AuthHistory[]> {
29
- const queryString = query ? `?${qs.stringify(query)}` : ''
30
- return this.client.get<Paginated<AuthHistory> | AuthHistory[]>(
31
- `/auth-history${queryString}`,
32
- headers,
33
- )
34
- }
35
-
36
- /**
37
- * Returns a single authentication event by its ID. Non-admin users can only access their own records.
38
- * @permissions authHistory.view
39
- */
40
- getById(id: string, headers?: Record<string, string>): Promise<AuthHistory> {
41
- return this.client.get<AuthHistory>(`/auth-history/${id}`, headers)
15
+ super(client, '/auth-history')
42
16
  }
43
17
  }
@@ -8,9 +8,10 @@ export type AuthHistory = {
8
8
  event: AuthHistoryEvent
9
9
  userId: string
10
10
  accountId: string
11
- branch: string
12
- originIP: string
13
- originUA: string
11
+ /** Nullable in the database. */
12
+ originIP: string | null
13
+ /** Nullable in the database. */
14
+ originUA: string | null
14
15
  createdAt: string
15
16
  updatedAt: string
16
17
  // relationships
@@ -96,7 +96,7 @@ describe('CampaignsApi', () => {
96
96
  await api.getMany({ page: 2, perPage: 5 })
97
97
  const url = vi.mocked(client.get).mock.calls[0][0] as string
98
98
  expect(url).toContain('/campaigns?')
99
- expect(url).toContain('page=2')
99
+ expect(decodeURIComponent(url)).toContain('"page":2')
100
100
  })
101
101
 
102
102
  it('forwards custom headers', async () => {
@@ -1,15 +1,13 @@
1
- import type { ApiClient } from '../../core/ApiClient'
1
+ import { BaseApi } from '../../core/BaseApi'
2
2
  import type { HasCreate } from '../../core/types'
3
3
  import type { ClientFeedback, CreateClientFeedbackPayload } from './types'
4
4
 
5
- export class ClientFeedbackApi implements HasCreate<ClientFeedback, CreateClientFeedbackPayload> {
6
- private readonly client: ApiClient
5
+ export class ClientFeedbackApi
6
+ extends BaseApi
7
+ implements HasCreate<ClientFeedback, CreateClientFeedbackPayload>
8
+ {
7
9
  private readonly basePath = '/client-feedback'
8
10
 
9
- constructor(client: ApiClient) {
10
- this.client = client
11
- }
12
-
13
11
  /** Creates a client feedback record for a ticket (accountId, ticketId, and feedback text are required). */
14
12
  create(
15
13
  body: CreateClientFeedbackPayload,
@@ -103,7 +103,7 @@ describe('ContactsApi', () => {
103
103
  await api.getMany({ page: 2, perPage: 5 })
104
104
  const url = vi.mocked(client.get).mock.calls[0][0] as string
105
105
  expect(url).toContain('/contacts?')
106
- expect(url).toContain('page=2')
106
+ expect(decodeURIComponent(url)).toContain('"page":2')
107
107
  })
108
108
 
109
109
  it('forwards custom headers', async () => {
@@ -35,7 +35,7 @@ export class ContactsApi extends BaseCrudApi<Contact, CreateContactPayload, Upda
35
35
  * @permissions contacts.view
36
36
  */
37
37
  count(query?: CountContactsQuery, headers?: Record<string, string>): Promise<{ count: number }> {
38
- const queryString = query ? `?${qs.stringify(query)}` : ''
38
+ const queryString = this.toQueryString(query)
39
39
  return this.client.get<{ count: number }>(`${this.basePath}/count${queryString}`, headers)
40
40
  }
41
41
 
@@ -62,7 +62,7 @@ export class ContactsApi extends BaseCrudApi<Contact, CreateContactPayload, Upda
62
62
  query?: ListQuery<Contact>,
63
63
  headers?: Record<string, string>,
64
64
  ): Promise<Paginated<Contact>> {
65
- const queryString = query ? `?${qs.stringify(query)}` : ''
65
+ const queryString = this.toQueryString(query)
66
66
  return this.client.get<Paginated<Contact>>(`${this.basePath}/forward${queryString}`, headers)
67
67
  }
68
68
 
@@ -17,33 +17,29 @@ export type CustomFieldType =
17
17
  | 'list'
18
18
 
19
19
  export type CustomFieldOption = {
20
- id?: string
21
- label?: string
20
+ id?: string | null
21
+ label?: string | null
22
22
  }
23
23
 
24
- /** Settings for `list` and `checkbox` types: a list of selectable options. */
25
- export type CustomFieldListSettings = {
26
- options: CustomFieldOption[]
24
+ /**
25
+ * Settings of a custom field — a free JSONB the backend does not validate, echoed back as sent.
26
+ * The fields below are the ones the web app form sends (`front/src/pages/custom-fields/components/
27
+ * form.tsx`): `options` for the `list` and `checkbox` types, `min`/`max` for `numeric` and `monetary`,
28
+ * `currency` for `monetary`, `maxLength` for `text` and `long-text`, and `placeholder` for the inputs.
29
+ */
30
+ export type CustomFieldSettings = {
31
+ options?: CustomFieldOption[]
32
+ placeholder?: string | null
33
+ currency?: string | null
34
+ maxLength?: number | null
35
+ min?: string | null
36
+ max?: string | null
27
37
  }
28
38
 
29
- /** Settings for `monetary` type: optional min/max value constraints. */
30
- export type CustomFieldMonetarySettings = {
31
- options: CustomFieldOption[]
32
- min?: string
33
- max?: string
34
- }
35
-
36
- /** Settings for `text` and `long-text` types: optional max length constraint. */
37
- export type CustomFieldTextSettings = {
38
- options: CustomFieldOption[]
39
- maxLength?: number
40
- }
41
-
42
- export type CustomFieldSettings =
43
- | CustomFieldListSettings
44
- | CustomFieldMonetarySettings
45
- | CustomFieldTextSettings
46
-
39
+ /**
40
+ * A custom field of the account. Custom fields are soft-deleted, but `deletedAt` is never returned
41
+ * (customFieldsTransformer).
42
+ */
47
43
  export type CustomField = {
48
44
  id: string
49
45
  name: string
@@ -55,7 +51,6 @@ export type CustomField = {
55
51
  accountId: string
56
52
  createdAt: string
57
53
  updatedAt: string
58
- deletedAt: string | null
59
54
  }
60
55
 
61
56
  export type CreateCustomFieldPayload = {
@@ -64,12 +59,13 @@ export type CreateCustomFieldPayload = {
64
59
  allowed: 'contacts'
65
60
  showOnRegister?: boolean
66
61
  required?: boolean
67
- settings?: CustomFieldSettings
62
+ settings?: CustomFieldSettings | null
68
63
  }
69
64
 
70
65
  export type UpdateCustomFieldPayload = {
71
66
  name?: string
72
67
  type?: CustomFieldType
68
+ allowed?: 'contacts'
73
69
  showOnRegister?: boolean
74
70
  required?: boolean
75
71
  settings?: CustomFieldSettings | null
@@ -0,0 +1,82 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { DashboardApi } from './DashboardApi'
3
+ import type { ApiClient } from '../../core/ApiClient'
4
+ import type { DashboardGeneral } from './types'
5
+
6
+ // ── Mock client factory ──────────────────────────────────────────────
7
+
8
+ function createMockClient(): ApiClient {
9
+ return {
10
+ setAccessToken: vi.fn().mockReturnThis(),
11
+ request: vi.fn(),
12
+ get: vi.fn(),
13
+ post: vi.fn(),
14
+ put: vi.fn(),
15
+ patch: vi.fn(),
16
+ delete: vi.fn(),
17
+ }
18
+ }
19
+
20
+ // ── Fixtures ─────────────────────────────────────────────────────────
21
+
22
+ const general: DashboardGeneral = {
23
+ sentMessagesCount: 10,
24
+ receivedMessagesCount: 5,
25
+ totalMessagesCount: 15,
26
+ openedTicketsCount: 2,
27
+ closedTicketsCount: 3,
28
+ totalTicketsCount: 5,
29
+ waitingTime: 60,
30
+ waitingTimeAfterBot: 30,
31
+ waitingTimeAvg: 45,
32
+ ticketTime: 600,
33
+ contactsCount: 4,
34
+ }
35
+
36
+ // ── Tests ────────────────────────────────────────────────────────────
37
+
38
+ describe('DashboardApi', () => {
39
+ let client: ReturnType<typeof createMockClient>
40
+ let api: DashboardApi
41
+
42
+ beforeEach(() => {
43
+ client = createMockClient()
44
+ api = new DashboardApi(client)
45
+ })
46
+
47
+ it('has no CRUD routes', () => {
48
+ expect(api.basePath).toBe('/dashboard')
49
+ expect(api).not.toHaveProperty('getMany')
50
+ })
51
+
52
+ describe('getGeneral', () => {
53
+ it('calls client.get with /dashboard/general and no query string', async () => {
54
+ vi.mocked(client.get).mockResolvedValue(general)
55
+ const result = await api.getGeneral()
56
+ expect(client.get).toHaveBeenCalledWith('/dashboard/general', undefined)
57
+ expect(result).toEqual(general)
58
+ })
59
+
60
+ it('sends the filters in the query string', async () => {
61
+ 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)
69
+ expect(client.get).toHaveBeenCalledWith(
70
+ `/dashboard/general?query=${encodeURIComponent(JSON.stringify(query))}`,
71
+ undefined,
72
+ )
73
+ })
74
+
75
+ it('forwards custom headers', async () => {
76
+ vi.mocked(client.get).mockResolvedValue(general)
77
+ const headers = { 'X-Custom': 'h' }
78
+ await api.getGeneral(undefined, headers)
79
+ expect(client.get).toHaveBeenCalledWith('/dashboard/general', headers)
80
+ })
81
+ })
82
+ })
@@ -0,0 +1,18 @@
1
+ import { BaseApi } from '../../core/BaseApi'
2
+ import type { DashboardGeneral, DashboardQuery } from './types'
3
+
4
+ /**
5
+ * Service statistics ("Estatísticas de atendimento"). The `/dashboard` routes only require an
6
+ * authenticated user (no permission check).
7
+ */
8
+ export class DashboardApi extends BaseApi {
9
+ public readonly basePath = '/dashboard'
10
+
11
+ /** Message, ticket, contact and time counts of the period (`GET /dashboard/general`). */
12
+ getGeneral(query?: DashboardQuery, headers?: Record<string, string>): Promise<DashboardGeneral> {
13
+ return this.client.get<DashboardGeneral>(
14
+ `${this.basePath}/general${this.toQueryString(query)}`,
15
+ headers,
16
+ )
17
+ }
18
+ }
@@ -0,0 +1,2 @@
1
+ export { DashboardApi } from './DashboardApi'
2
+ export type * from './types'
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Filters of the service statistics (`/dashboard`). Every field is optional; the period defaults to
3
+ * the last 7 days.
4
+ */
5
+ export type DashboardQuery = {
6
+ /** ISO date-time. Default: 7 days ago, 00:00. */
7
+ startPeriod?: string
8
+ /** ISO date-time. Default: the end of the `startPeriod` day (or of today). */
9
+ endPeriod?: string
10
+ /** Which ticket date the period filters: opening (default) or closing. */
11
+ periodType?: 'openDate' | 'closeDate'
12
+ /** Ticket status (`closed`, not `close`). */
13
+ status?: 'all' | 'open' | 'closed'
14
+ /**
15
+ * A department id, a list of ids, or `all`. Only applied together with `departmentParticipation`:
16
+ * without it the backend ignores `departmentId` and answers the totals of every department.
17
+ */
18
+ departmentId?: string | string[]
19
+ /**
20
+ * Whether the department must be the ticket's last one or any one it went through. Required for
21
+ * `departmentId` to filter; sent WITHOUT `departmentId`, it filters the tickets with no department.
22
+ */
23
+ departmentParticipation?: 'last' | 'middle'
24
+ /**
25
+ * A user id, a list of ids, or `all`. Only applied together with `userParticipation`: without it
26
+ * the backend ignores `userId` and answers the totals of every user.
27
+ */
28
+ userId?: string | string[]
29
+ /**
30
+ * Whether the user must be the ticket's last one or any one it went through. Required for `userId`
31
+ * to filter; sent WITHOUT `userId`, it filters the tickets with no user.
32
+ */
33
+ userParticipation?: 'last' | 'middle'
34
+ userStatus?: 'active' | 'archived' | 'all'
35
+ serviceId?: string | string[]
36
+ tagId?: string | string[]
37
+ ticketTopicId?: string | string[]
38
+ }
39
+
40
+ /** `GET /dashboard/general` — counts of the period. Times are in seconds. */
41
+ export type DashboardGeneral = {
42
+ sentMessagesCount: number
43
+ receivedMessagesCount: number
44
+ totalMessagesCount: number
45
+ openedTicketsCount: number
46
+ closedTicketsCount: number
47
+ totalTicketsCount: number
48
+ waitingTime: number
49
+ waitingTimeAfterBot: number
50
+ waitingTimeAvg: number
51
+ ticketTime: number
52
+ contactsCount: number
53
+ }
@@ -1,25 +1,53 @@
1
+ import type { Account } from '../accounts/types'
2
+ import type { File } from '../files/types'
3
+
4
+ /**
5
+ * A holiday: the account's services answer `message` during the period `from`–`to`.
6
+ * Holidays are soft-deleted, but `deletedAt` is never returned.
7
+ */
1
8
  export type Holiday = {
2
9
  id: string
3
10
  name: string
4
11
  from: string
5
12
  to: string
6
13
  message: string
14
+ /** Whether the period is over. */
15
+ expired: boolean
16
+ /** Number of attached files (computed on the listing, derived from `files` on the detail). */
17
+ filesCount: number
18
+ /**
19
+ * Attached files — always an array. Only loaded on `getById` (or when listed with
20
+ * `include: ['files']`): `getMany` answers `[]` even when the holiday has files, so use
21
+ * `filesCount` there.
22
+ */
23
+ files: File[]
7
24
  accountId: string
8
25
  createdAt: string
9
- updatedAt: string | null
10
- deletedAt: string | null
26
+ /** Virtual attribute of the model — may be absent. */
27
+ updatedAt?: string | null
28
+ // relationships
29
+ account?: Account
11
30
  }
12
31
 
32
+ export type HolidayRelationships = 'account' | 'files'
33
+
34
+ /** A file attached by id (up to 10 per holiday). */
35
+ export type HolidayFileRef = { id: string }
36
+
13
37
  export type CreateHolidayPayload = {
38
+ /** Up to 255 characters. */
14
39
  name: string
40
+ /** Date string. A period overlapping another holiday answers 402 "Holiday time already exists.". */
15
41
  from: string
42
+ /** Date string. */
16
43
  to: string
44
+ /** Up to 1024 characters. */
17
45
  message: string
46
+ files?: HolidayFileRef[]
18
47
  }
19
48
 
20
- export type UpdateHolidayPayload = {
21
- name?: string
22
- from?: string
23
- to?: string
24
- message?: string
25
- }
49
+ /**
50
+ * The backend requires name, from, to and message on every update (same validator as the create).
51
+ * Omitting `files` keeps the current attachments.
52
+ */
53
+ export type UpdateHolidayPayload = CreateHolidayPayload
package/src/apis/index.ts CHANGED
@@ -15,6 +15,7 @@ export * from './contactBlockListsControls'
15
15
  export * from './contacts'
16
16
  export * from './creditMovements'
17
17
  export * from './customFields'
18
+ export * from './dashboard'
18
19
  export * from './departments'
19
20
  export * from './distribution'
20
21
  export * from './files'
@@ -29,6 +30,7 @@ export * from './knowledgeBaseItem'
29
30
  export * from './me'
30
31
  export * from './messages'
31
32
  export * from './notifications'
33
+ export * from './now'
32
34
  export * from './organizations'
33
35
  export * from './people'
34
36
  export * from './permissions'
@@ -1,4 +1,4 @@
1
- import type { ApiClient } from '../../core/ApiClient'
1
+ import { BaseApi } from '../../core/BaseApi'
2
2
  import type { Account } from '../accounts/types'
3
3
  import type { User } from '../users/types'
4
4
  import type {
@@ -10,13 +10,7 @@ import type {
10
10
  UpdateMeStatusPayload,
11
11
  } from './types'
12
12
 
13
- export class MeApi {
14
- protected readonly client: ApiClient
15
-
16
- constructor(client: ApiClient) {
17
- this.client = client
18
- }
19
-
13
+ export class MeApi extends BaseApi {
20
14
  /**
21
15
  * Returns the currently authenticated user, including their roles, departments, and timetable.
22
16
  * @permissions users.view
@@ -87,8 +87,8 @@ describe('MessagesApi', () => {
87
87
  await api.getMany({ page: 2, perPage: 5 })
88
88
  const url = vi.mocked(client.get).mock.calls[0][0] as string
89
89
  expect(url).toContain('/messages?')
90
- expect(url).toContain('page=2')
91
- expect(url).toContain('perPage=5')
90
+ expect(decodeURIComponent(url)).toContain('"page":2')
91
+ expect(decodeURIComponent(url)).toContain('"perPage":5')
92
92
  })
93
93
 
94
94
  it('forwards custom headers', async () => {
@@ -0,0 +1,125 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { NowApi } from './NowApi'
3
+ import type { ApiClient } from '../../core/ApiClient'
4
+ import type {
5
+ NowAttendanceResume,
6
+ NowDepartmentResume,
7
+ NowResponse,
8
+ NowResumeTotals,
9
+ } from './types'
10
+
11
+ // ── Mock client factory ──────────────────────────────────────────────
12
+
13
+ function createMockClient(): ApiClient {
14
+ return {
15
+ setAccessToken: vi.fn().mockReturnThis(),
16
+ request: vi.fn(),
17
+ get: vi.fn(),
18
+ post: vi.fn(),
19
+ put: vi.fn(),
20
+ patch: vi.fn(),
21
+ delete: vi.fn(),
22
+ }
23
+ }
24
+
25
+ // ── Fixtures ─────────────────────────────────────────────────────────
26
+
27
+ const resume: NowResponse<NowResumeTotals> = {
28
+ totals: {
29
+ openTickets: 3,
30
+ queueTickets: 1,
31
+ usersWithAttendance: 2,
32
+ usersWithoutAttendance: 1,
33
+ averageOpenTicketsTime: '125.5',
34
+ averageOpenTicketsWaitTime: 0,
35
+ onlineUsersCount: 2,
36
+ offlineUsersCount: 4,
37
+ absentUsersCount: 0,
38
+ },
39
+ }
40
+ const attendance: NowResponse<NowAttendanceResume[]> = {
41
+ totals: [{ name: 'Ana', departments: ['Suporte'], status: 'online', ticketsOpenCount: 2 }],
42
+ }
43
+ const departments: NowResponse<NowDepartmentResume[]> = {
44
+ totals: [
45
+ {
46
+ name: 'Suporte',
47
+ onlineUserCount: 1,
48
+ offlineUserCount: 0,
49
+ absentUserCount: 0,
50
+ onQueueTicketCount: 1,
51
+ openTicketCount: 2,
52
+ },
53
+ ],
54
+ }
55
+
56
+ const jsonQuery = (query: object) => `?query=${encodeURIComponent(JSON.stringify(query))}`
57
+
58
+ // ── Tests ────────────────────────────────────────────────────────────
59
+
60
+ describe('NowApi', () => {
61
+ let client: ReturnType<typeof createMockClient>
62
+ let api: NowApi
63
+
64
+ beforeEach(() => {
65
+ client = createMockClient()
66
+ api = new NowApi(client)
67
+ })
68
+
69
+ it('has no CRUD routes', () => {
70
+ expect(api.basePath).toBe('/now')
71
+ expect(api).not.toHaveProperty('getMany')
72
+ expect(api).not.toHaveProperty('create')
73
+ })
74
+
75
+ describe('getResume', () => {
76
+ it('calls client.get with /now/resume and no query string', async () => {
77
+ vi.mocked(client.get).mockResolvedValue(resume)
78
+ const result = await api.getResume()
79
+ expect(client.get).toHaveBeenCalledWith('/now/resume', undefined)
80
+ expect(result).toEqual(resume)
81
+ })
82
+
83
+ it('sends the department/user filters in the query string', async () => {
84
+ vi.mocked(client.get).mockResolvedValue(resume)
85
+ await api.getResume({ departmentId: ['d1', 'd2'], userId: 'u1' })
86
+ expect(client.get).toHaveBeenCalledWith(
87
+ `/now/resume${jsonQuery({ departmentId: ['d1', 'd2'], userId: 'u1' })}`,
88
+ undefined,
89
+ )
90
+ })
91
+
92
+ it('forwards custom headers', async () => {
93
+ vi.mocked(client.get).mockResolvedValue(resume)
94
+ const headers = { 'X-Custom': 'h' }
95
+ await api.getResume(undefined, headers)
96
+ expect(client.get).toHaveBeenCalledWith('/now/resume', headers)
97
+ })
98
+ })
99
+
100
+ describe('getAttendanceResume', () => {
101
+ it('calls client.get with /now/attendance-resume', async () => {
102
+ vi.mocked(client.get).mockResolvedValue(attendance)
103
+ const result = await api.getAttendanceResume({ departmentId: 'd1' })
104
+ expect(client.get).toHaveBeenCalledWith(
105
+ `/now/attendance-resume${jsonQuery({ departmentId: 'd1' })}`,
106
+ undefined,
107
+ )
108
+ expect(result).toEqual(attendance)
109
+ })
110
+ })
111
+
112
+ describe('getDepartmentsResume', () => {
113
+ it('calls client.get with /now/departments-resume', async () => {
114
+ vi.mocked(client.get).mockResolvedValue(departments)
115
+ const result = await api.getDepartmentsResume()
116
+ expect(client.get).toHaveBeenCalledWith('/now/departments-resume', undefined)
117
+ expect(result).toEqual(departments)
118
+ })
119
+
120
+ it('propagates errors from client.get', async () => {
121
+ vi.mocked(client.get).mockRejectedValue(new Error('Forbidden'))
122
+ await expect(api.getDepartmentsResume()).rejects.toThrow('Forbidden')
123
+ })
124
+ })
125
+ })
@@ -0,0 +1,59 @@
1
+ import { BaseApi } from '../../core/BaseApi'
2
+ import type {
3
+ NowAttendanceResume,
4
+ NowDepartmentResume,
5
+ NowQuery,
6
+ NowResponse,
7
+ NowResumeTotals,
8
+ } from './types'
9
+
10
+ /**
11
+ * The "Agora" (now) overview: live totals of tickets and attendants. Every route requires
12
+ * `overview.now.view.all` or `overview.now.view.departments` (the latter is scoped to the user's
13
+ * departments).
14
+ */
15
+ export class NowApi extends BaseApi {
16
+ public readonly basePath = '/now'
17
+
18
+ /**
19
+ * Account totals: open/queued tickets, attendants with/without attendance and online/offline/absent users.
20
+ * @permissions overview.now.view.all | overview.now.view.departments
21
+ */
22
+ getResume(
23
+ query?: NowQuery,
24
+ headers?: Record<string, string>,
25
+ ): Promise<NowResponse<NowResumeTotals>> {
26
+ return this.client.get<NowResponse<NowResumeTotals>>(
27
+ `${this.basePath}/resume${this.toQueryString(query)}`,
28
+ headers,
29
+ )
30
+ }
31
+
32
+ /**
33
+ * Per-attendant totals (name, departments, status and open tickets).
34
+ * @permissions overview.now.view.all | overview.now.view.departments
35
+ */
36
+ getAttendanceResume(
37
+ query?: NowQuery,
38
+ headers?: Record<string, string>,
39
+ ): Promise<NowResponse<NowAttendanceResume[]>> {
40
+ return this.client.get<NowResponse<NowAttendanceResume[]>>(
41
+ `${this.basePath}/attendance-resume${this.toQueryString(query)}`,
42
+ headers,
43
+ )
44
+ }
45
+
46
+ /**
47
+ * Per-department totals (online/offline/absent users, queued and open tickets).
48
+ * @permissions overview.now.view.all | overview.now.view.departments
49
+ */
50
+ getDepartmentsResume(
51
+ query?: NowQuery,
52
+ headers?: Record<string, string>,
53
+ ): Promise<NowResponse<NowDepartmentResume[]>> {
54
+ return this.client.get<NowResponse<NowDepartmentResume[]>>(
55
+ `${this.basePath}/departments-resume${this.toQueryString(query)}`,
56
+ headers,
57
+ )
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ export { NowApi } from './NowApi'
2
+ export type * from './types'