@autobusal/admin-api-consumers 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Browse.tsx CHANGED
@@ -3,7 +3,7 @@ import { Link, useSearchParams } from 'react-router-dom';
3
3
  import { Meta, Table, Row, Button } from '@autobusal/common';
4
4
  import { usePage, useBreadcrumbs } from '@autobusal/hooks';
5
5
  import { DisplayStatus, getParams, success } from '@autobusal/utilities';
6
- import { useGetApiConsumers, useDeleteApiConsumer } from './services';
6
+ import { useGetApiConsumers, useDeleteApiConsumer, useDeclineApiRequest } from './services';
7
7
 
8
8
  interface Props {
9
9
  url: string
@@ -17,10 +17,32 @@ const Browse = ({ url, t }: Props): JSX.Element => {
17
17
 
18
18
  useBreadcrumbs(t('admin_api_consumers.browse.title', { ns: 'common' }));
19
19
 
20
- const { data, isLoading, isFetching, refetch } = useGetApiConsumers(getParams(searchParams, ['page', 'sort', 'order', 'q']));
20
+ /*
21
+ * Edited: Claude - Date: 2026-08-28
22
+ *
23
+ * ?type=requests flips this screen to the P8 self-serve queue: agents
24
+ * who asked for partner API access and await a ruling. Approving one is
25
+ * the ordinary manage screen on the same row - review, save, and the
26
+ * save sets the consumer flag - so the only new verb here is Decline.
27
+ */
28
+ const requests = searchParams.get('type') === 'requests';
29
+
30
+ const { data, isLoading, isFetching, refetch } = useGetApiConsumers(getParams(searchParams, ['page', 'sort', 'order', 'q', 'type']));
21
31
 
22
32
  const { mutate: Delete } = useDeleteApiConsumer();
23
33
 
34
+ const { mutate: Decline } = useDeclineApiRequest();
35
+
36
+ const onDecline = (id: number): void => {
37
+ Decline(id, {
38
+ onSuccess: () => {
39
+ success(t('admin_api_consumers.requests.declined', { ns: 'common' }));
40
+
41
+ refetch();
42
+ }
43
+ });
44
+ };
45
+
24
46
  const onDelete = (id: number): void => {
25
47
  Delete(id, {
26
48
  onSuccess: () => {
@@ -35,7 +57,21 @@ const Browse = ({ url, t }: Props): JSX.Element => {
35
57
  <Row
36
58
  key={ item.id }
37
59
  id={ item.id }
38
- data={ [
60
+ data={ requests ? [
61
+ item.company,
62
+ item.user.email,
63
+ item.api_requested_at ? String(item.api_requested_at).slice(0, 10) : '-',
64
+ DisplayStatus(item.user.status, t),
65
+ <Button
66
+ key="decline"
67
+ type="button"
68
+ size="small"
69
+ subtype="secondary"
70
+ noMargin
71
+ text={ t('admin_api_consumers.requests.decline', { ns: 'common' }) }
72
+ onClick={ () => onDecline(item.id) }
73
+ />
74
+ ] : [
39
75
  item.company,
40
76
  item.user.email,
41
77
  item.operators.includes('all') ? t('admin_api_consumers.browse.all_operators', { ns: 'common' }) : String(item.operators.length),
@@ -51,7 +87,24 @@ const Browse = ({ url, t }: Props): JSX.Element => {
51
87
 
52
88
  <Table
53
89
  url={ url }
54
- columns={ [{
90
+ columns={ requests ? [{
91
+ name: t('admin_api_consumers.browse.company', { ns: 'common' }),
92
+ width: 28,
93
+ slug: 'company',
94
+ type: 'az'
95
+ }, {
96
+ name: t('admin_api_consumers.browse.email', { ns: 'common' }),
97
+ width: 28
98
+ }, {
99
+ name: t('admin_api_consumers.requests.requested', { ns: 'common' }),
100
+ width: 16
101
+ }, {
102
+ name: t('admin_api_consumers.browse.status', { ns: 'common' }),
103
+ width: 14
104
+ }, {
105
+ name: '',
106
+ width: 14
107
+ }] : [{
55
108
  name: t('admin_api_consumers.browse.company', { ns: 'common' }),
56
109
  width: 25,
57
110
  slug: 'company',
@@ -83,9 +136,23 @@ const Browse = ({ url, t }: Props): JSX.Element => {
83
136
  'create', 'search', 'extra', 'view', 'delete'
84
137
  ] }
85
138
  extra={
86
- <Link to={ `${ url }/receivables` }>
87
- <Button type="button" size="medium" subtype="secondary" noMargin text={ t('admin_api_consumers.receivables.title', { ns: 'common' }) } />
88
- </Link>
139
+ <>
140
+ <Link to={ requests ? url : `${ url }?type=requests` }>
141
+ <Button
142
+ type="button"
143
+ size="medium"
144
+ subtype="secondary"
145
+ noMargin
146
+ text={ requests
147
+ ? t('admin_api_consumers.requests.back', { ns: 'common' })
148
+ : t('admin_api_consumers.requests.title', { ns: 'common' }) }
149
+ />
150
+ </Link>
151
+
152
+ <Link to={ `${ url }/receivables` }>
153
+ <Button type="button" size="medium" subtype="secondary" noMargin text={ t('admin_api_consumers.receivables.title', { ns: 'common' }) } />
154
+ </Link>
155
+ </>
89
156
  }
90
157
  handlers={ {
91
158
  delete: onDelete
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.2 (2026-08-28)
4
+
5
+ - Requests view (?type=requests): the P8 self-serve queue - agents awaiting a partner-API ruling, with Decline per row; approving stays the ordinary manage/save flow. Toggle button beside Receivables.
6
+
7
+ ## 1.2.1
8
+
9
+ ### Fixed
10
+
11
+ - **The token panel updates without a reload** - toggle/revoke/regenerate invalidate the consumer they changed. The button also reads "Generate Token" while no token exists. (Audit ADM-04f.)
12
+
3
13
  ## 1.2.0
4
14
 
5
15
  ### Added
package/TokenManager.tsx CHANGED
@@ -78,7 +78,7 @@ const TokenManager = ({ id, hasToken, enabled, t }: Props): JSX.Element => {
78
78
  subtype="secondary"
79
79
  noMargin
80
80
  loading={ isPendingRegenerate }
81
- text={ <><FiRefreshCw /> { t('admin_api_consumers.token.regenerate', { ns: 'common' }) }</> }
81
+ text={ <><FiRefreshCw /> { t(hasToken ? 'admin_api_consumers.token.regenerate' : 'admin_api_consumers.token.generate', { ns: 'common' }) }</> }
82
82
  onClick={ onRegenerate }
83
83
  />
84
84
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobusal/admin-api-consumers",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "author": "Ferjolt Ozuni",
5
5
  "type": "module",
6
6
  "main": "index.ts"
package/services.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { UseMutationResult, UseQueryResult, UseSuspenseQueryResult, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
1
+ import { UseMutationResult, UseQueryResult, UseSuspenseQueryResult, useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
2
2
  import { apiClient } from '@autobusal/providers';
3
3
  import { PaginationData } from '@autobusal/providers/types/pagination';
4
4
  import { TableParamsData } from '@autobusal/providers/types/other';
@@ -91,6 +91,23 @@ export const usePostApiConsumer = (id: number): UseMutationResult<{ api_token: s
91
91
  })
92
92
  );
93
93
 
94
+ /**
95
+ * Decline a pending P8 access request - clears the ask, grants nothing.
96
+ * Edited: Claude - Date: 2026-08-28
97
+ */
98
+ export const useDeclineApiRequest = (): UseMutationResult<void, Error, number, unknown> => (
99
+ useMutation({
100
+ mutationKey: ['admin-api-consumers-decline'],
101
+ mutationFn: async (id: number) => (
102
+ await apiClient
103
+ .post('/api/api_consumers/admin/decline', { id })
104
+ .then(response => (
105
+ response.data
106
+ ))
107
+ )
108
+ })
109
+ );
110
+
94
111
  export const useDeleteApiConsumer = (): UseMutationResult<void, Error, number, unknown> => (
95
112
  useMutation({
96
113
  mutationKey: ['admin-api-consumers-delete'],
@@ -106,8 +123,17 @@ export const useDeleteApiConsumer = (): UseMutationResult<void, Error, number, u
106
123
  })
107
124
  );
108
125
 
109
- export const useToggleApiConsumer = (): UseMutationResult<void, Error, { id: number, api: number }, unknown> => (
110
- useMutation({
126
+ /*
127
+ * Claude - 2026-08-22 (audit finding ADM-04f): the token panel only showed
128
+ * a toggle/revoke/regenerate's effect after a full page reload - an admin
129
+ * could reasonably believe Revoke had failed. Each mutation now
130
+ * invalidates the consumer it changed, so the panel redraws from the
131
+ * server's answer immediately.
132
+ */
133
+ export const useToggleApiConsumer = (): UseMutationResult<void, Error, { id: number, api: number }, unknown> => {
134
+ const client = useQueryClient();
135
+
136
+ return useMutation({
111
137
  mutationKey: ['admin-api-consumers-toggle'],
112
138
  mutationFn: async ({ id, api }: { id: number, api: number }) => (
113
139
  await apiClient
@@ -115,12 +141,17 @@ export const useToggleApiConsumer = (): UseMutationResult<void, Error, { id: num
115
141
  .then(response => (
116
142
  response.data
117
143
  ))
118
- )
119
- })
120
- );
144
+ ),
145
+ onSuccess: (_, { id }): void => {
146
+ client.invalidateQueries({ queryKey: ['admin-api-consumers-manage', { id }] });
147
+ }
148
+ });
149
+ };
121
150
 
122
- export const useRevokeApiConsumerToken = (): UseMutationResult<void, Error, number, unknown> => (
123
- useMutation({
151
+ export const useRevokeApiConsumerToken = (): UseMutationResult<void, Error, number, unknown> => {
152
+ const client = useQueryClient();
153
+
154
+ return useMutation({
124
155
  mutationKey: ['admin-api-consumers-revoke'],
125
156
  mutationFn: async (id: number) => (
126
157
  await apiClient
@@ -128,12 +159,17 @@ export const useRevokeApiConsumerToken = (): UseMutationResult<void, Error, numb
128
159
  .then(response => (
129
160
  response.data
130
161
  ))
131
- )
132
- })
133
- );
162
+ ),
163
+ onSuccess: (_, id): void => {
164
+ client.invalidateQueries({ queryKey: ['admin-api-consumers-manage', { id }] });
165
+ }
166
+ });
167
+ };
134
168
 
135
- export const useRegenerateApiConsumerToken = (): UseMutationResult<{ api_token: string }, Error, number, unknown> => (
136
- useMutation({
169
+ export const useRegenerateApiConsumerToken = (): UseMutationResult<{ api_token: string }, Error, number, unknown> => {
170
+ const client = useQueryClient();
171
+
172
+ return useMutation({
137
173
  mutationKey: ['admin-api-consumers-regenerate'],
138
174
  mutationFn: async (id: number) => (
139
175
  await apiClient
@@ -141,9 +177,12 @@ export const useRegenerateApiConsumerToken = (): UseMutationResult<{ api_token:
141
177
  .then(response => (
142
178
  response.data
143
179
  ))
144
- )
145
- })
146
- );
180
+ ),
181
+ onSuccess: (_, id): void => {
182
+ client.invalidateQueries({ queryKey: ['admin-api-consumers-manage', { id }] });
183
+ }
184
+ });
185
+ };
147
186
 
148
187
  // Edited: Ferjolt Ozuni - Date: 2026-07-27
149
188
  // Package 3 - API partner receivables reports + settlements
package/types.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ComissionData } from '@autobusal/providers/types/users';
2
2
 
3
3
  export interface ApiConsumerData {
4
+ api_requested_at?: (string | null)
4
5
  id: number
5
6
  company: string
6
7
  operators: (number | string)[]