11import * as React from "react" ;
2- import { useState } from "react" ;
2+ import { useEffect , useState } from "react" ;
33import {
44 Badge ,
55 Combobox ,
66 Option ,
77 OptionGroup ,
88 Spinner ,
99} from "@fluentui/react-components" ;
10- import { Resolve } from "@paperbits/react/decorators" ;
1110import { GroupByTag } from "../../../../utils/react/TableListInfo" ;
12- import { Pagination } from "../../../../utils/react/Pagination" ;
1311import * as Constants from "../../../../../constants" ;
1412import { Api } from "../../../../../models/api" ;
15- import {
16- isApisGrouped ,
17- TagGroupToggleBtn ,
18- TApisData ,
19- toggleValueInSet ,
20- } from "./utils" ;
21- import { RouteHelper } from "../../../../../routing/routeHelper" ;
22- import { ApiService } from "../../../../../services/apiService" ;
13+ import { Tag } from "../../../../../models/tag" ;
14+ import { TagGroup } from "../../../../../models/tagGroup" ;
15+ import { Page } from "../../../../../models/page" ;
16+ import { SearchQuery } from "../../../../../contracts/searchQuery" ;
2317import { TApiListRuntimeFCProps } from "./ApiListRuntime" ;
18+ import { TagGroupToggleBtn , toggleValueInSet } from "./utils" ;
2419
2520type TApiListDropdown = Omit <
2621 TApiListRuntimeFCProps ,
27- "apiService" | "layoutDefault" | "productName"
28- > & {
29- working : boolean ;
30- apis : TApisData ;
31- statePageNumber : ReturnType < typeof useState < number > > ;
32- statePattern : ReturnType < typeof useState < string > > ;
33- stateGroupByTag : ReturnType < typeof useState < boolean > > ;
34- } ;
22+ "tagService" | "layoutDefault" | "productName"
23+ > ;
3524
3625const TagLabel = ( {
3726 tag,
@@ -76,22 +65,92 @@ const Options = ({
7665 </ >
7766) ;
7867
79- const ApiListDropdownFC = ( {
80- working,
81- apis,
68+ export const ApiListDropdown = ( {
69+ apiService,
8270 getReferenceUrl,
8371 selectedApi,
84- statePageNumber : [ pageNumber , setPageNumber ] ,
85- statePattern : [ _ , setPattern ] ,
86- stateGroupByTag : [ groupByTag , setGroupByTag ] ,
72+ defaultGroupByTagToEnabled
8773} : TApiListDropdown & { selectedApi ?: Api } ) => {
8874 const [ expanded , setExpanded ] = React . useState ( new Set < string > ( ) ) ;
89-
90- const pageMax = Math . ceil ( apis ?. count / Constants . defaultPageSize ) ;
75+ const [ working , setWorking ] = useState ( false ) ;
76+ const [ pageNumber , setPageNumber ] = useState ( 1 ) ;
77+ const [ hasNextPage , setHasNextPage ] = useState < boolean > ( false ) ;
78+ const [ apis , setApis ] = useState < Api [ ] > ( [ ] ) ;
79+ const [ apisByTag , setApisByTag ] = useState < TagGroup < Api > [ ] > ( [ ] ) ;
80+ const [ pattern , setPattern ] = useState < string > ( ) ;
81+ const [ groupByTag , setGroupByTag ] = useState ( ! ! defaultGroupByTagToEnabled ) ;
82+ const [ filters , setFilters ] = useState < { tags : Tag [ ] } > ( { tags : [ ] } ) ;
9183
9284 const toggleTag = ( tag : string ) =>
9385 setExpanded ( ( old ) => toggleValueInSet ( old , tag ) ) ;
9486
87+ useEffect ( ( ) => {
88+ const query : SearchQuery = {
89+ pattern,
90+ tags : filters . tags ,
91+ skip : ( pageNumber - 1 ) * Constants . defaultPageSize ,
92+ take : Constants . defaultPageSize
93+ } ;
94+
95+ setWorking ( true ) ;
96+ if ( groupByTag ) {
97+ loadApisByTag ( query )
98+ . then ( loadedApis => {
99+ if ( pageNumber > 1 ) {
100+ // Check if the tag is already displayed. If yes, add to this tag
101+ loadedApis . value . forEach ( newApi => {
102+ const existingTagIndex = apisByTag . findIndex ( item => item . tag === newApi . tag ) ;
103+ if ( existingTagIndex !== - 1 ) {
104+ apisByTag [ existingTagIndex ] . items . push ( ...newApi . items ) ;
105+ } else {
106+ apisByTag . push ( newApi ) ;
107+ }
108+ } ) ;
109+ setApisByTag ( apisByTag ) ;
110+ } else {
111+ setApisByTag ( [ ...loadedApis . value ] ) ;
112+ }
113+ setHasNextPage ( ! ! loadedApis . nextLink ) ;
114+ } )
115+ . finally ( ( ) => setWorking ( false ) ) ;
116+ } else {
117+ loadApis ( query )
118+ . then ( loadedApis => {
119+ if ( pageNumber > 1 ) {
120+ setApis ( [ ...apis , ...loadedApis . value ] ) ;
121+ } else {
122+ setApis ( [ ...loadedApis . value ] ) ;
123+ }
124+ setHasNextPage ( ! ! loadedApis . nextLink ) ;
125+ } )
126+ . finally ( ( ) => setWorking ( false ) ) ;
127+ }
128+ } , [ apiService , pageNumber , groupByTag , filters , pattern ] ) ;
129+
130+ const loadApis = async ( query : SearchQuery ) => {
131+ let apis : Page < Api > ;
132+
133+ try {
134+ apis = await apiService . getApis ( query ) ;
135+ } catch ( error ) {
136+ throw new Error ( `Unable to load APIs. Error: ${ error . message } ` ) ;
137+ }
138+
139+ return apis ;
140+ }
141+
142+ const loadApisByTag = async ( query : SearchQuery ) => {
143+ let apis : Page < TagGroup < Api > > ;
144+
145+ try {
146+ apis = await apiService . getApisByTags ( query ) ;
147+ } catch ( error ) {
148+ throw new Error ( `Unable to load APIs. Error: ${ error . message } ` ) ;
149+ }
150+
151+ return apis ;
152+ }
153+
95154 const content = ! apis || ! selectedApi ? (
96155 < > Loading APIs</ > // if data are not loaded yet ComboBox sometimes fails to initialize properly - edge case, in most cases almost instant from the cache
97156 ) : (
@@ -119,15 +178,19 @@ const ApiListDropdownFC = ({
119178 disabled
120179 value = { "group by tag switch" }
121180 text = { "group by tag switch" }
181+ style = { { columnGap : 0 } }
182+ className = "group-by-tag-switch"
122183 >
123184 < GroupByTag
124185 groupByTag = { groupByTag }
125186 setGroupByTag = { setGroupByTag }
187+ setPageNumber = { setPageNumber }
188+ labelAfter
126189 />
127190 </ Option >
128191
129- { isApisGrouped ( apis ) ? (
130- apis ?. value . map ( ( { tag, items } ) => (
192+ { groupByTag ? (
193+ apisByTag ? .map ( ( { tag, items } ) => (
131194 < OptionGroup
132195 key = { tag }
133196 label = {
@@ -148,22 +211,20 @@ const ApiListDropdownFC = ({
148211 ) )
149212 ) : (
150213 < Options
151- apis = { apis . value }
214+ apis = { apis }
152215 getReferenceUrl = { getReferenceUrl }
153216 />
154217 ) }
155218
156- { pageMax > 1 && (
219+ { hasNextPage && (
157220 < Option
158221 disabled
159222 value = { "pagination" }
160223 text = { "pagination" }
224+ checkIcon = { < > </ > }
225+ style = { { columnGap : 0 } }
161226 >
162- < Pagination
163- pageNumber = { pageNumber }
164- setPageNumber = { setPageNumber }
165- pageMax = { pageMax }
166- />
227+ < button className = { "button button-default show-more-options" } onClick = { ( ) => setPageNumber ( prev => prev + 1 ) } > Show more</ button >
167228 </ Option >
168229 ) }
169230 </ >
@@ -178,49 +239,3 @@ const ApiListDropdownFC = ({
178239 </ >
179240 ) ;
180241} ;
181-
182- export class ApiListDropdown extends React . Component <
183- TApiListDropdown ,
184- { working : boolean ; api ?: Api }
185- > {
186- @Resolve ( "apiService" )
187- public apiService : ApiService ;
188-
189- @Resolve ( "routeHelper" )
190- public routeHelper : RouteHelper ;
191-
192- constructor ( props : TApiListDropdown ) {
193- super ( props ) ;
194-
195- this . state = {
196- working : false ,
197- api : undefined ,
198- } ;
199- }
200-
201- public componentDidMount ( ) {
202- this . loadSelectedApi ( ) ;
203- }
204-
205- async loadSelectedApi ( ) {
206- const apiName = this . routeHelper . getApiName ( ) ;
207- if ( ! apiName ) return ;
208-
209- this . setState ( { working : true , api : undefined } ) ;
210-
211- return this . apiService
212- . getApi ( `apis/${ apiName } ` )
213- . then ( ( api ) => this . setState ( { api } ) )
214- . finally ( ( ) => this . setState ( { working : false } ) ) ;
215- }
216-
217- render ( ) {
218- return (
219- < ApiListDropdownFC
220- { ...this . props }
221- working = { this . props . working || this . state . working }
222- selectedApi = { this . state . api }
223- />
224- ) ;
225- }
226- }
0 commit comments