config#Config TypeScript Examples

The following examples show how to use config#Config. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: App.tsx    From one-platform with MIT License 6 votes vote down vote up
client = createClient({
  url: config.apiURL,
  requestPolicy: 'cache-and-network',
  maskTypename: true,
  fetchOptions: () => {
    const token = opcBase.auth?.jwtToken;
    return {
      headers: {
        authorization: token ? `Bearer ${token}` : '',
      },
    };
  },
})
Example #2
Source File: App.tsx    From one-platform with MIT License 6 votes vote down vote up
App = () => {
  return (
    <Provider value={client}>
      <RecentVisitProvider>
        <BrowserRouter basename={config.baseURL}>
          <BreadcrumbProvider>
            <Suspense fallback={<Loader />}>
              <opc-provider>
                <opc-nav />
                <opc-menu-drawer />
                <opc-notification-drawer />
                <opc-feedback />
              </opc-provider>
              <Router />
            </Suspense>
          </BreadcrumbProvider>
        </BrowserRouter>
      </RecentVisitProvider>
    </Provider>
  );
}
Example #3
Source File: others.routing.ts    From matx-angular with MIT License 6 votes vote down vote up
OthersRoutes: Routes = [
  {
    path: '',
    children: [{
      path: 'blank',
      canActivate: [UserRoleGuard],
      component: AppBlankComponent,
      data: { title: 'Blank', breadcrumb: 'Blank', roles: config.authRoles.sa }
    }]
  }
]
Example #4
Source File: appLayout.helper.ts    From one-platform with MIT License 6 votes vote down vote up
handleBreadCrumbLinkGenerator = (url: string): BreadCrumbLink[] => {
  const currentUrl = url.slice(config.baseURL.length);
  const breadCrumbLinks: BreadCrumbLink[] = [];

  currentUrl.split('/').forEach((urlSub) => {
    if (urlSub && urlLookUpTable?.[urlSub]) {
      breadCrumbLinks.push(urlLookUpTable[urlSub]);
    }
  });

  return breadCrumbLinks;
}
Example #5
Source File: saga.ts    From oasis-wallet-web with Apache License 2.0 6 votes vote down vote up
/**
 * Return the explorer APIs for the specified network
 * or by default, for the currently selected network
 */
export function* getExplorerAPIs(network?: NetworkType) {
  const selectedNetwork = yield* select(selectSelectedNetwork)
  const url = config[selectedNetwork][backend()].explorer
  return backendApi(url)
}
Example #6
Source File: saga.ts    From oasis-wallet-web with Apache License 2.0 6 votes vote down vote up
/**
 * Return a nic client for the specified network,
 * or by default, for the currently selected network
 */
export function* getOasisNic(network?: NetworkType) {
  let selectedNetwork = network ? network : yield* select(selectSelectedNetwork)
  const url = config[selectedNetwork].grpc

  let nic = new oasis.client.NodeInternal(url)
  return nic
}
Example #7
Source File: ApiTypeSelector.tsx    From one-platform with MIT License 6 votes vote down vote up
ApiTypeSelector = ({ value, onChange, errorMsg }: Props): JSX.Element => {
  return (
    <FormGroup fieldId="datasource-type" label="Datasource type" isRequired>
      <Split hasGutter>
        {apiOptions.map(({ title, desc, image, type }) => (
          <SplitItem isFilled key={type}>
            <CatalogBigButton
              title={title}
              desc={desc}
              image={`${config.baseURL}/images/${image}`}
              isSelected={value === type}
              onClick={callbackify(onChange, type)}
            />
          </SplitItem>
        ))}
      </Split>
      {errorMsg && (
        <FormAlert>
          <Alert variant="danger" title={errorMsg} aria-live="polite" isInline />
        </FormAlert>
      )}
    </FormGroup>
  );
}
Example #8
Source File: ApiTypeCard.tsx    From one-platform with MIT License 6 votes vote down vote up
ApiTypeCard = ({ category = ApiCategory.REST }: Props): JSX.Element => {
  const getApiTypeImage = useCallback(
    (apiCategory: ApiCategory) =>
      apiCategory === ApiCategory.REST
        ? `${config.baseURL}/images/rest-logo.svg`
        : `${config.baseURL}/images/graphql-logo.svg`,
    []
  );

  return (
    <Card className="catalog-card-flat">
      <CardBody>
        <Split className="pf-u-align-items-center">
          <SplitItem isFilled>
            <Stack>
              <StackItem className="pf-u-mb-xs">
                <Text component={TextVariants.small}>API Type</Text>
              </StackItem>
              <StackItem>
                <Title headingLevel="h6" size="2xl">
                  {category}
                </Title>
              </StackItem>
            </Stack>
          </SplitItem>
          <SplitItem>
            <img src={getApiTypeImage(category)} alt="api-type" />
          </SplitItem>
        </Split>
      </CardBody>
    </Card>
  );
}
Example #9
Source File: environment.ts    From matx-angular with MIT License 5 votes vote down vote up
environment = {
  production: false,
  apiURL: config.apiUrl
}
Example #10
Source File: app.ts    From one-platform with MIT License 5 votes vote down vote up
startApolloServer = async (gqlSchema: GraphQLSchema, config: Config) => {
  const context: ContextFunction<FastifyContext> = async ({ request }) => {
    const id = request?.headers?.['x-op-user-id'];
    const loaders = {
      user: setupUserDataLoader(config.apiGatewayURL, config.apiGatewayToken),
      subscriptionStatus: setupSubscriptionStatusLoader(),
    };

    return { loaders, user: { id } };
  };

  const app = fastify({
    logger: true,
  });

  const server = new ApolloServer({
    schema: gqlSchema,
    context,
    dataSources: () => {
      return {
        namespaceDB: new NamespaceDB(Namespace),
        subscriptionDB: new SubscriptionDB(Subscription),
        specStoreDB: new SpecStoreDB(SpecStore),
      };
    },
    formatError: (error) => ({
      message: error.message,
      locations: error.locations,
      path: error.path,
      ...error.extensions,
    }),
    plugins: [
      fastifyAppClosePlugin(app),
      ApolloServerPluginDrainHttpServer({ httpServer: app.server }),
    ],
  });

  await server.start();
  app.register(server.createHandler());
  await app.listen(config.port, '0.0.0.0');
}
Example #11
Source File: environment.prod.ts    From matx-angular with MIT License 5 votes vote down vote up
environment = {
  production: true,
  apiURL: config.apiUrl
}
Example #12
Source File: HomePage.tsx    From one-platform with MIT License 5 votes vote down vote up
REST_LOGO = `${config.baseURL}/images/rest-logo.svg`
Example #13
Source File: HomePage.tsx    From one-platform with MIT License 5 votes vote down vote up
GRAPHQL_LOGO = `${config.baseURL}/images/graphql-logo.svg`
Example #14
Source File: HomePage.tsx    From one-platform with MIT License 5 votes vote down vote up
OP_CONTAINER_LOGO = `${config.baseURL}/images/op-containers.svg`
Example #15
Source File: ApiEnvironmentSection.tsx    From one-platform with MIT License 5 votes vote down vote up
REDOC_LOGO = `${config.baseURL}/images/redoc-logo.png`
Example #16
Source File: ApiEnvironmentSection.tsx    From one-platform with MIT License 5 votes vote down vote up
PLAYGROUND_ICON = `${config.baseURL}/images/gql-playground-logo.svg`
Example #17
Source File: ApiEnvironmentSection.tsx    From one-platform with MIT License 5 votes vote down vote up
SWAGGER_ICON = `${config.baseURL}/images/swagger-logo.svg`
Example #18
Source File: AppLayout.tsx    From one-platform with MIT License 5 votes vote down vote up
AppLayout: FC = () => {
  const { pathname } = useLocation();
  const [isSidebarOpen, setIsSidebarOpen] = useToggle(true);
  const { breadcrumbs } = useBreadcrumb();
  const isBreadcrumbHidden = breadcrumbs.length === 0;

  return (
    <Page
      mainContainerId="app-layout-page"
      sidebar={<Sidebar isOpen={isSidebarOpen} />}
      className={styles['app-layout']}
      breadcrumb={
        !isBreadcrumbHidden && (
          <Breadcrumb className={styles['app-layout--breadcrumb']}>
            <BreadcrumbItem>
              <Button variant="link" className="pf-u-p-0" onClick={setIsSidebarOpen.toggle}>
                <BarsIcon />
              </Button>
            </BreadcrumbItem>
            <BreadcrumbItem to="/">One Platform</BreadcrumbItem>
            <BreadcrumbItem>
              {pathname === config.baseURL ? 'API Catalog Home' : <Link to="/">API Catalog</Link>}
            </BreadcrumbItem>
            {breadcrumbs.map(({ label, url }, index) => {
              const isActive = index === breadcrumbs.length - 1;
              return (
                <BreadcrumbItem key={label}>
                  {isActive ? label : <Link to={url}>{label}</Link>}
                </BreadcrumbItem>
              );
            })}
          </Breadcrumb>
        )
      }
    >
      <PageSection
        className={styles['app-layout--content']}
        variant="light"
        padding={{ default: 'noPadding' }}
      >
        <Outlet />
      </PageSection>
    </Page>
  );
}
Example #19
Source File: BreadcrumbContext.tsx    From one-platform with MIT License 5 votes vote down vote up
BreadcrumbProvider = ({ children }: Props): JSX.Element => {
  // load it up
  const [dynamicCrumbs, setDynamicCrumbs] = useState<Record<string, Breadcrumb>>();
  const [breadcrumbs, setBreadcrumbs] = useState<Breadcrumb[]>([]);
  const { pathname } = useLocation();

  const handleDynamicCrumbs = useCallback((crumb: Record<string, Breadcrumb>): void => {
    // save recent visit to quick link
    setDynamicCrumbs((state) => ({ ...state, ...crumb }));
  }, []);

  useEffect(() => {
    const path = pathname.replace(config.baseURL, '');
    const matchedPath = (Object.keys(BREADCRUMB_USERFLOW) as ApiCatalogLinks[]).find((pattern) =>
      Boolean(matchPath(pattern, path))
    );
    if (matchedPath) {
      const crumbs = BREADCRUMB_USERFLOW[matchedPath]
        .map((userflow) =>
          LOOK_UP_TABLE?.[userflow] ? LOOK_UP_TABLE?.[userflow] : dynamicCrumbs?.[userflow]
        )
        .filter((crumb) => Boolean(crumb)) as Breadcrumb[];
      setBreadcrumbs(crumbs);
    } else {
      setBreadcrumbs([]);
    }
  }, [pathname, dynamicCrumbs]);

  /**
   * Context value to access glabally from anywhere
   * Memo to optimize at best
   */
  const value = useMemo(
    () => ({
      breadcrumbs,
      handleDynamicCrumbs,
    }),

    [breadcrumbs, handleDynamicCrumbs]
  );

  return <BreadcrumbContext.Provider value={value}>{children}</BreadcrumbContext.Provider>;
}
Example #20
Source File: App.tsx    From one-platform with MIT License 5 votes vote down vote up
opcBase.configure({
  apiBasePath: config.opcBase.apiBasePath,
  subscriptionsPath: config.opcBase.subscriptionsPath,
  keycloakUrl: config.opcBase.keycloakUrl,
  keycloakClientId: config.opcBase.keycloakClientId,
  keycloakRealm: config.opcBase.keycloakRealm,
});
Example #21
Source File: ApiSchemaList.tsx    From one-platform with MIT License 4 votes vote down vote up
ApiSchemaList = ({ schemas = [], onClick, selectedSchemaID }: Props): JSX.Element => {
  const numberOfGraphqlAPI = schemas.reduce(
    (prev, { category }) => (category === ApiCategory.GRAPHQL ? prev + 1 : prev),
    0
  );

  const numberOfRestAPI = schemas.reduce(
    (prev, { category }) => (category === ApiCategory.REST ? prev + 1 : prev),
    0
  );

  return (
    <Card
      isCompact
      isRounded
      style={{ maxWidth: '320px', marginLeft: 'auto' }}
      className="border-1"
    >
      <CardHeader>
        <Stack hasGutter className="pf-u-w-100">
          <StackItem className="pf-u-text-align-center">
            <CardTitle>API Schemas Registered</CardTitle>
          </StackItem>
          <StackItem>
            <Split hasGutter>
              <SplitItem isFilled>
                <Stack>
                  <StackItem>
                    <Text component={TextVariants.small}>Total</Text>
                    <Title headingLevel="h3" size="3xl">
                      {schemas.length}
                    </Title>
                  </StackItem>
                </Stack>
              </SplitItem>
              <Divider isVertical />
              <SplitItem isFilled>
                <Stack>
                  <StackItem>
                    <Text component={TextVariants.small}>REST</Text>
                    <Title headingLevel="h3" size="3xl">
                      {numberOfRestAPI}
                    </Title>
                  </StackItem>
                </Stack>
              </SplitItem>
              <Divider isVertical />
              <SplitItem isFilled>
                <Stack>
                  <StackItem>
                    <Text component={TextVariants.small}>GraphQL</Text>
                    <Title headingLevel="h3" size="3xl">
                      {numberOfGraphqlAPI}
                    </Title>
                  </StackItem>
                </Stack>
              </SplitItem>
            </Split>
          </StackItem>
        </Stack>
      </CardHeader>
      <CardBody>
        <DataList
          isCompact
          aria-label="schema-list"
          style={{ overflowY: 'auto', maxHeight: '180px' }}
        >
          {schemas.map(({ id: sID, name, category, flags }) => (
            <DataListItem key={sID}>
              <DataListItemRow
                className={css('pf-u-px-sm cursor-pointer', {
                  'menu-selected': selectedSchemaID === sID,
                })}
                onClick={() => onClick(sID)}
              >
                <DataListItemCells
                  dataListCells={[
                    <DataListCell key={`${sID}-category`} isFilled={false}>
                      <Avatar
                        src={`${config.baseURL}/images/${
                          category === 'REST' ? 'swagger-black-logo.svg' : 'graphql-logo.svg'
                        }`}
                        alt="api-type"
                        size="sm"
                        style={{ width: '1rem', height: '1rem' }}
                        className="pf-u-mt-sm"
                      />
                    </DataListCell>,
                    <DataListCell
                      key={`${sID}-${name}`}
                      className="pf-u-display-flex pf-u-align-items-center"
                    >
                      <Text>{name}</Text>
                    </DataListCell>,
                    <DataListCell
                      isFilled={false}
                      key={`${sID}-flag`}
                      className="pf-u-display-flex pf-u-align-items-center"
                    >
                      <Label
                        color={flags.isInternal ? 'blue' : 'green'}
                        isCompact
                        className="pf-u-ml-sm"
                      >
                        {flags.isInternal ? 'Internal' : 'External'}
                      </Label>
                    </DataListCell>,
                  ]}
                />
              </DataListItemRow>
            </DataListItem>
          ))}
        </DataList>
      </CardBody>
    </Card>
  );
}
Example #22
Source File: APIListPage.tsx    From one-platform with MIT License 4 votes vote down vote up
APIListPage = (): JSX.Element => {
  const navigate = useNavigate();

  // query param strings
  const query = useQueryParams();
  const mid = query.get('mid');
  const defaultSearch = query.get('search');

  // filters, search, sorting
  const [isSortSelectOpen, setSortSelect] = useToggle();
  const [sortOption, setSortOption] = useState(SortBy.RECENTLY_ADDED);
  const [filters, setFilters] = useState<{ type: null | ApiCategory; search: string }>({
    type: null,
    search: defaultSearch || '',
  });
  const { pagination, onPerPageSelect, onSetPage, onResetPagination } = usePagination({
    page: 1,
    perPage: 20,
  });
  const debouncedSearch = useDebounce(filters.search);

  // graphql query hooks
  const { isLoading: isApiListLoading, data: namespaceList } = useGetNamespaceList({
    limit: pagination.perPage,
    offset: (pagination.page - 1) * pagination.perPage,
    apiCategory: filters.type,
    search: debouncedSearch,
    sortBy: sortOption === SortBy.RECENTLY_ADDED ? 'CREATED_ON' : 'UPDATED_ON',
    mid,
  });
  const { isLoading: isNamespaceStatLoading, data: namespaceStats } = useGetNamespaceStats({
    search: debouncedSearch,
    mid,
  });

  const handleApiOwnersRender = useCallback((owners: ApiOwnerType[]) => {
    return owners.map((owner) =>
      owner.group === ApiEmailGroup.USER ? owner.user.cn : owner.email
    );
  }, []);

  const onStatCardClick = (cardType: 'total' | 'rest' | 'graphql') => {
    onResetPagination();
    if (cardType === 'total') {
      setFilters((state) => ({ ...state, type: null }));
    } else {
      setFilters((state) => ({ ...state, type: cardType.toUpperCase() as ApiCategory }));
    }
  };

  const onSearch = (search: string) => {
    setFilters((state) => ({ ...state, search }));
  };

  const onSortSelect = (
    event: React.MouseEvent | React.ChangeEvent,
    value: string | SelectOptionObject,
    isPlaceholder?: boolean
  ) => {
    if (isPlaceholder) setSortOption(SortBy.RECENTLY_ADDED);
    else setSortOption(value as SortBy);

    setSortSelect.off(); // close the select
  };

  const onCardClick = (id: string) => {
    navigate(id);
  };

  const namespaceCount = namespaceStats?.getApiCategoryCount;
  const namespaces = namespaceList?.listNamespaces?.data;

  const isNamespaceEmpty = !isApiListLoading && namespaces?.length === 0;

  return (
    <>
      <Header />
      <Divider />
      <PageSection variant="light" isWidthLimited className="pf-m-align-center">
        <Grid hasGutter>
          <Grid hasGutter span={12}>
            {stats.map(({ key, type, image }) => (
              <GridItem
                key={`api-select-${type}`}
                span={4}
                className={styles['api-list--stat-card']}
                type={key}
              >
                <StatCard
                  value={namespaceCount?.[key]}
                  category={type}
                  isLoading={isNamespaceStatLoading}
                  onClick={callbackify(onStatCardClick, key)}
                  isSelected={filters.type ? filters.type.toLowerCase() === key : key === 'total'}
                >
                  <img
                    src={`${config.baseURL}/images/${image}`}
                    alt={`api-select-${type}`}
                    style={{ height: '48px' }}
                  />
                </StatCard>
              </GridItem>
            ))}
          </Grid>
          <GridItem className="pf-u-my-md">
            <Split hasGutter className={styles['api-list--table-filter--container']}>
              <SplitItem isFilled>
                <Link to={ApiCatalogLinks.AddNewApiPage}>
                  <Button>Add API</Button>
                </Link>
              </SplitItem>
              <SplitItem className="pf-u-w-33">
                <Form>
                  <FormGroup fieldId="search">
                    <TextInput
                      aria-label="Search API"
                      placeholder="Search for APIs"
                      type="search"
                      iconVariant="search"
                      value={filters.search}
                      onChange={onSearch}
                    />
                  </FormGroup>
                </Form>
              </SplitItem>
              <SplitItem style={{ width: '180px' }}>
                <Select
                  isOpen={isSortSelectOpen}
                  onToggle={setSortSelect.toggle}
                  selections={sortOption}
                  onSelect={onSortSelect}
                >
                  {[
                    <SelectOption key="select-sort-placeholder" value="Sort by" isDisabled />,
                    <SelectOption
                      key={`select-sort:${SortBy.RECENTLY_ADDED}`}
                      value={SortBy.RECENTLY_ADDED}
                    />,
                    <SelectOption
                      key={`select-sort:${SortBy.RECENTLY_MODIFIED}`}
                      value={SortBy.RECENTLY_MODIFIED}
                    />,
                  ]}
                </Select>
              </SplitItem>
            </Split>
          </GridItem>

          {isApiListLoading ? (
            <Bullseye className="pf-u-mt-lg">
              <Spinner size="xl" />
            </Bullseye>
          ) : (
            namespaces?.map(({ id, name, updatedOn, owners, schemas, slug }) => (
              <GridItem
                span={12}
                key={id}
                className="catalog-nav-link"
                onClick={callbackify(onCardClick, slug)}
              >
                <ApiDetailsCard
                  title={name}
                  owners={handleApiOwnersRender(owners)}
                  updatedAt={updatedOn}
                  schemas={schemas.map(({ name: schemaName, category }) => ({
                    name: schemaName,
                    type: category,
                  }))}
                />
              </GridItem>
            ))
          )}
          {isNamespaceEmpty && (
            <EmptyState>
              <EmptyStateIcon icon={CubesIcon} />
              <Title headingLevel="h4" size="lg">
                No API found
              </Title>
              <EmptyStateBody>Add an API to fill this gap</EmptyStateBody>
            </EmptyState>
          )}
        </Grid>
      </PageSection>
      <PageSection variant="light" isWidthLimited className="pf-m-align-center pf-u-pb-2xl">
        <Pagination
          itemCount={namespaceList?.listNamespaces?.count || 0}
          widgetId="pagination-options-menu-bottom"
          perPage={pagination.perPage}
          page={pagination.page}
          onSetPage={(_, page) => onSetPage(page)}
          onPerPageSelect={(_, perPage) => onPerPageSelect(perPage)}
          isCompact
        />
      </PageSection>
    </>
  );
}
Example #23
Source File: ApiDetailsCard.tsx    From one-platform with MIT License 4 votes vote down vote up
ApiDetailsCard = ({
  title,
  owners = [],
  schemas = [],
  updatedAt,
}: Props): JSX.Element => {
  const formatUpdatedAt = useCallback(
    (apiUpdatedAt: string) => `modified on: ${dayjs(apiUpdatedAt).format('DD MMM YYYY hh:mm a')}`,
    []
  );

  const stopOnClickPropogation: DOMAttributes<HTMLDivElement>['onClick'] = (event) => {
    event.stopPropagation();
  };

  return (
    <Card className="catalog-card-hover-effect cursor-pointer">
      <CardBody className="pf-u-px-md">
        <Stack hasGutter>
          <StackItem>
            <Split hasGutter className="pf-l-flex pf-m-align-items-flex-end">
              <SplitItem isFilled>
                <Title headingLevel="h4" size={TitleSizes['2xl']}>
                  {title}
                </Title>
              </SplitItem>
              <SplitItem>
                <Text component={TextVariants.small} className="pf-u-color-400">
                  {formatUpdatedAt(updatedAt)}
                </Text>
              </SplitItem>
            </Split>
          </StackItem>
          <StackItem>
            <Split hasGutter>
              <SplitItem>
                <Split isWrappable onClick={stopOnClickPropogation}>
                  <SplitItem className="pf-u-mr-xs">
                    <Text>Owned by:</Text>
                  </SplitItem>
                  <ReadMore
                    limit={MAX_LOADED}
                    showMoreText={`+${(owners || []).length - MAX_LOADED} more`}
                  >
                    {owners.map((owner) => (
                      <SplitItem className="pf-u-ml-xs" key={owner}>
                        <Label isCompact color="cyan">
                          {owner}
                        </Label>
                      </SplitItem>
                    ))}
                  </ReadMore>
                </Split>
              </SplitItem>
              <SplitItem isFilled />
              <SplitItem>
                <Split onClick={stopOnClickPropogation} isWrappable>
                  <SplitItem className="pf-u-mr-sm">
                    <Text>Schema(s): </Text>
                  </SplitItem>
                  <ReadMore
                    limit={MAX_LOADED}
                    showMoreText={`+${(schemas || []).length - MAX_LOADED} more`}
                  >
                    {schemas.map(({ name, type }) => (
                      <SplitItem style={{ marginTop: '0.1rem' }} key={name}>
                        <Label
                          color="blue"
                          className="pf-u-mr-xs"
                          isCompact
                          icon={
                            <img
                              src={`${config.baseURL}/images/${
                                type === 'REST' ? 'swagger-black-logo.svg' : 'graphql-logo.svg'
                              }`}
                              alt="api-type"
                              className="pf-u-mt-xs"
                              style={{ height: '0.8rem', width: '0.8rem' }}
                            />
                          }
                        >
                          {name}
                        </Label>
                      </SplitItem>
                    ))}
                  </ReadMore>
                </Split>
              </SplitItem>
            </Split>
          </StackItem>
        </Stack>
      </CardBody>
    </Card>
  );
}
Example #24
Source File: APIDescriptionPage.tsx    From one-platform with MIT License 4 votes vote down vote up
APIDescriptionPage = (): JSX.Element => {
  const { slug } = useParams();
  const navigate = useNavigate();
  const { handleDynamicCrumbs } = useBreadcrumb();
  const urlParser = useURLParser();
  const [selectedSchemaIndex, setSelectedSchemaIndex] = useState(0);
  const [isSubscriptionOptionOpen, setIsSubscriptionOptionOpen] = useToggle();
  const [isSchemaDropdownOpen, setIsSchemaDropdownOpen] = useToggle();
  const [selectedSubscriptonEnv, setSelectedSubscriptionEnv] = useState<Record<
    string,
    boolean
  > | null>(null);
  const userInfo = opcBase.auth?.getUserInfo();

  const [{ fetching: isSubscribing, data: subscribedNamespace }, handleSubscribeSchemaGQL] =
    useSubscribeSchema();
  const { isLoading: isNamespaceLoading, data: fetchedNamespace } = useGetANamespaceBySlug({
    slug,
  });

  const namespace = subscribedNamespace?.subscribeApiSchema || fetchedNamespace?.getNamespaceBySlug;
  const id = namespace?.id;
  const schemas = namespace?.schemas || [];
  const selectedSchema = namespace?.schemas[selectedSchemaIndex];

  // effect to add breadcrumb data
  useEffect(() => {
    if (!isNamespaceLoading && namespace?.name && namespace?.id) {
      handleDynamicCrumbs({
        'api-name': { label: namespace.name, url: `/apis/${namespace?.slug}` },
      });
    }

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isNamespaceLoading, namespace?.name, namespace?.id]);

  const hasEditAccess = useMemo(() => {
    const userUuid = userInfo?.rhatUUID;
    return hasUserApiEditAccess(userUuid as string, namespace);
  }, [namespace, userInfo?.rhatUUID]);

  const onMenuClick = (schemaId: string) => {
    const index = namespace?.schemas.findIndex(({ id: sid }) => sid === schemaId);
    if (index !== -1) {
      setSelectedSchemaIndex(index || 0);
    }
    setIsSchemaDropdownOpen.off();
  };

  const hasSubscribed = selectedSchema?.environments.some(({ isSubscribed }) => isSubscribed);

  const handleSchemaSubscription = async (envIDs?: string[]) => {
    const subscribedIds =
      envIDs ||
      (hasSubscribed ? [] : (selectedSchema?.environments || []).map(({ id: sID }) => sID));
    const subscriptionConfig = {
      namespaceID: id as string,
      schemaID: selectedSchema?.id || '',
      envIDs: subscribedIds,
      email: userInfo?.email || '',
    };
    try {
      const res = await handleSubscribeSchemaGQL({ config: subscriptionConfig });
      if (res.error) {
        opcBase.toast.danger({
          subject: `Failed to ${hasSubscribed ? 'unsubscribe' : 'subscribe'} api`,
          body: res?.error?.message,
        });
      } else {
        const subject = `${hasSubscribed ? 'Unsubscribed' : 'Subscribed'} to ${namespace?.name}`;
        const body = `You will ${
          hasSubscribed ? 'not be' : 'be'
        } be notified regarding updates on this API`;
        opcBase.toast.info({ subject, body });
      }
    } catch (error) {
      opcBase.toast.danger({
        subject: `Failed to ${hasSubscribed ? 'unsubscribe' : 'subscribe'} api`,
      });
    }
  };

  const onInitializeSelect = () => {
    if (!isSubscriptionOptionOpen) {
      const alreadySubscripedEnv = (selectedSchema?.environments || []).reduce<
        Record<string, boolean>
      >(
        (prev, { isSubscribed, id: eId }) =>
          isSubscribed ? { ...prev, [eId]: true } : { ...prev },
        {}
      );
      setSelectedSubscriptionEnv(alreadySubscripedEnv);
    }
    setIsSubscriptionOptionOpen.toggle();
  };

  const onEnvSelect = (env: string) => {
    const isEnvPresent = Boolean(selectedSubscriptonEnv?.[env]);
    const state = { ...selectedSubscriptonEnv };
    if (isEnvPresent) {
      delete state[env];
      setSelectedSubscriptionEnv(state);
    } else {
      state[env] = true;
      setSelectedSubscriptionEnv(state);
    }
  };

  const onEnvSelectBlur = async () => {
    if (selectedSubscriptonEnv) {
      await handleSchemaSubscription(Object.keys(selectedSubscriptonEnv));
    }
    setIsSubscriptionOptionOpen.off();
    setSelectedSubscriptionEnv(null);
  };

  if (isNamespaceLoading) {
    return (
      <Bullseye>
        <Spinner size="xl" />
      </Bullseye>
    );
  }

  if (!namespace) {
    return (
      <Bullseye>
        <EmptyState>
          <EmptyStateIcon icon={CubesIcon} />
          <Title headingLevel="h4" size="lg">
            Sorry, Couldn&apos;t find this API
          </Title>
          <Button variant="primary" onClick={() => navigate('../')}>
            Go Back
          </Button>
        </EmptyState>
      </Bullseye>
    );
  }

  return (
    <Stack>
      <StackItem>
        <PageSection isWidthLimited isCenterAligned>
          <Grid hasGutter>
            <GridItem span={8}>
              <DetailsSection namespace={namespace} id={slug} hasEditAccess={hasEditAccess} />
            </GridItem>
            <GridItem span={4}>
              <ApiSchemaList
                schemas={namespace?.schemas}
                onClick={onMenuClick}
                selectedSchemaID={selectedSchema?.id}
              />
            </GridItem>
          </Grid>
        </PageSection>
      </StackItem>
      <StackItem>
        <PageSection
          isWidthLimited
          isCenterAligned
          padding={{ default: 'noPadding' }}
          className="pf-u-py-sm pf-u-px-md"
        >
          <Text component={TextVariants.small} className="pf-u-color-400">
            API Schema
          </Text>
        </PageSection>
      </StackItem>
      <StackItem>
        <Divider />
      </StackItem>
      <StackItem>
        <PageSection isWidthLimited isCenterAligned className="pf-u-pb-4xl">
          <Grid hasGutter>
            {selectedSchema?.flags.isDeprecated && (
              <Grid span={12}>
                <Alert variant="danger" isInline title={`${selectedSchema.name} is deprecated`} />
              </Grid>
            )}
            <GridItem span={8}>
              <Stack
                hasGutter
                style={{ '--pf-l-stack--m-gutter--MarginBottom': '1.5rem' } as CSSProperties}
              >
                <StackItem className={styles.schemaContainer}>
                  <Split>
                    <SplitItem isFilled>
                      <Button
                        variant="link"
                        icon={<CaretDownIcon />}
                        onClick={setIsSchemaDropdownOpen.toggle}
                        iconPosition="right"
                        style={{ color: 'black' }}
                        className={styles.schemaDropdownTitle}
                      >
                        {selectedSchema?.name}
                      </Button>
                    </SplitItem>
                    <SplitItem className="pf-u-mr-lg">
                      <Label color={selectedSchema?.flags?.isInternal ? 'blue' : 'green'} isCompact>
                        {selectedSchema?.flags?.isInternal ? 'Internal API' : 'External API'}
                      </Label>
                    </SplitItem>
                  </Split>
                  <CSSTransition
                    in={isSchemaDropdownOpen}
                    timeout={200}
                    classNames="fade-in"
                    unmountOnExit
                  >
                    <Menu className={styles.schemaMenu}>
                      <MenuContent>
                        <MenuList className="pf-u-py-0">
                          {schemas.map((schema, index) => (
                            <Fragment key={schema.id}>
                              <MenuItem
                                className={css({
                                  'menu-selected': schema.id === selectedSchema?.id,
                                })}
                                icon={
                                  <Avatar
                                    src={`${config.baseURL}/images/${
                                      schema.category === 'REST'
                                        ? 'swagger-black-logo.svg'
                                        : 'graphql-logo.svg'
                                    }`}
                                    alt="api-type"
                                    size="sm"
                                    style={{ width: '1.25rem', height: '1.25rem' }}
                                    className="pf-u-mt-sm"
                                  />
                                }
                                onClick={() => onMenuClick(schema.id)}
                              >
                                <Split>
                                  <SplitItem isFilled>{schema.name}</SplitItem>
                                  <SplitItem>
                                    <Label
                                      color={schema.flags.isInternal ? 'blue' : 'green'}
                                      isCompact
                                      className="pf-u-ml-sm"
                                    >
                                      {schema.flags.isInternal ? 'Internal' : 'External'}
                                    </Label>
                                  </SplitItem>
                                </Split>
                              </MenuItem>
                              {schemas.length - 1 !== index && (
                                <Divider component="li" className="pf-u-my-0" />
                              )}
                            </Fragment>
                          ))}
                        </MenuList>
                      </MenuContent>
                    </Menu>
                  </CSSTransition>
                </StackItem>
                <StackItem>
                  <ReadMore>{selectedSchema?.description || ''}</ReadMore>
                </StackItem>
                <StackItem>
                  <Split hasGutter>
                    <SplitItem isFilled>
                      <Title headingLevel="h3">Application URL</Title>
                      <a href={selectedSchema?.appURL} target="_blank" rel="noopener noreferrer">
                        <Text className="pf-u-color-400">
                          {urlParser(selectedSchema?.appURL || '')}
                        </Text>
                      </a>
                    </SplitItem>
                    <SplitItem isFilled>
                      <Title headingLevel="h3">Documentation URL</Title>
                      <a href={selectedSchema?.docURL} target="_blank" rel="noopener noreferrer">
                        <Text className="pf-u-color-400">
                          {urlParser(selectedSchema?.docURL || '')}
                        </Text>
                      </a>
                    </SplitItem>
                  </Split>
                </StackItem>
                <StackItem className="pf-u-mt-md">
                  <ApiEnvironmentSection
                    environments={selectedSchema?.environments}
                    category={selectedSchema?.category}
                  />
                </StackItem>
              </Stack>
            </GridItem>
            <GridItem span={1} />
            <GridItem span={3}>
              <Stack hasGutter>
                <StackItem className={styles.subscriptionContainer}>
                  <Split>
                    <SplitItem isFilled>
                      <Button
                        icon={<BellIcon />}
                        variant={hasSubscribed ? 'primary' : 'secondary'}
                        iconPosition="right"
                        isBlock
                        isLoading={isSubscribing}
                        className={css(hasSubscribed ? styles.subscriptionDropdownBtn : null)}
                        onClick={() => handleSchemaSubscription()}
                      >
                        {hasSubscribed ? 'Subscribed' : 'Subscribe'}
                      </Button>
                    </SplitItem>
                    <CSSTransition
                      in={hasSubscribed}
                      timeout={200}
                      classNames="fade-in"
                      unmountOnExit
                    >
                      <SplitItem>
                        <Button
                          icon={<CaretDownIcon />}
                          onClick={onInitializeSelect}
                          className={css('ignore-blur', styles.subscriptionDropdownArrow)}
                        />
                      </SplitItem>
                    </CSSTransition>
                  </Split>
                  <CSSTransition
                    in={isSubscriptionOptionOpen}
                    timeout={200}
                    classNames="fade-in"
                    unmountOnExit
                  >
                    <Menu
                      className={styles.subscriptionMenu}
                      onBlur={(e) => {
                        if (!e.relatedTarget?.className?.includes('ignore-blur')) {
                          onEnvSelectBlur();
                        }
                      }}
                    >
                      <MenuContent>
                        <MenuList className="pf-u-py-0">
                          {selectedSchema?.environments.map(({ name, isSubscribed, id: envId }) => (
                            <MenuItem
                              className="uppercase ignore-blur"
                              isSelected={
                                selectedSubscriptonEnv
                                  ? selectedSubscriptonEnv[envId]
                                  : isSubscribed
                              }
                              key={`subscription-${envId}`}
                              itemId={envId}
                              onClick={() => onEnvSelect(envId)}
                            >
                              {name}
                            </MenuItem>
                          ))}
                        </MenuList>
                      </MenuContent>
                    </Menu>
                  </CSSTransition>
                </StackItem>
                <StackItem>
                  <ApiTypeCard category={selectedSchema?.category} />
                </StackItem>
              </Stack>
            </GridItem>
          </Grid>
        </PageSection>
      </StackItem>
    </Stack>
  );
}
Example #25
Source File: index.tsx    From oasis-wallet-web with Apache License 2.0 4 votes vote down vote up
export function Transaction(props: TransactionProps) {
  const { t } = useTranslation()
  const transaction = props.transaction
  const referenceAddress = props.referenceAddress
  const amount = <AmountFormatter amount={transaction.amount!} />

  let side: TransactionSide
  let otherAddress = ''

  if (transaction.from === referenceAddress) {
    side = TransactionSide.Sent
    otherAddress = transaction.to!
  } else {
    side = TransactionSide.Received
    otherAddress = transaction.from!
  }

  const unrecognizedTransaction: TransactionDictionary[transactionTypes.TransactionType][TransactionSide] = {
    designation: t('account.otherTransaction.designation', 'Other address'),
    icon: () => <New />,
    header: () => (
      <Trans
        i18nKey="account.otherTransaction.header"
        t={t}
        values={{ method: transaction.type }}
        defaults="Unrecognized transaction, method '{{method}}'"
      />
    ),
  }

  // @TODO: This could probably cleverly be moved outside of the component
  //for better readability and marginal performance gain, but for now
  //the translation keys need to be read by i18next extraction
  const transactionDictionary: TransactionDictionary = {
    [transactionTypes.TransactionType.StakingTransfer]: {
      [TransactionSide.Received]: {
        designation: t('common.from', 'From'),
        icon: () => <LinkPrevious />,
        header: () => (
          <Trans
            i18nKey="account.transaction.transfer.received"
            t={t}
            components={[amount]}
            defaults="Received <0></0>"
          />
        ),
      },
      [TransactionSide.Sent]: {
        designation: t('common.to', 'To'),
        icon: () => <LinkNext />,
        header: () => (
          <Trans
            i18nKey="account.transaction.transfer.sent.header"
            t={t}
            components={[amount]}
            defaults="Sent <0></0>"
          />
        ),
      },
    },
    [transactionTypes.TransactionType.StakingAddEscrow]: {
      [TransactionSide.Received]: {
        designation: t('common.delegator', 'Delegator'),
        icon: () => <LineChart />,
        header: () => (
          <Trans
            i18nKey="account.transaction.addEscrow.received"
            t={t}
            components={[amount]}
            defaults="Received <0></0> delegation in escrow"
          />
        ),
      },
      [TransactionSide.Sent]: {
        designation: t('common.validator', 'Validator'),
        icon: () => <LineChart />,
        header: () => (
          <Trans
            i18nKey="account.transaction.addEscrow.sent"
            t={t}
            components={[amount]}
            defaults="Delegated <0></0> to validator"
          />
        ),
      },
    },
    [transactionTypes.TransactionType.StakingReclaimEscrow]: {
      [TransactionSide.Received]: {
        designation: t('common.delegator', 'Delegator'),
        icon: () => <Money />,
        header: () => (
          <Trans
            i18nKey="account.transaction.reclaimEscrow.received"
            t={t}
            components={[amount]}
            defaults="<0></0> reclaimed by delegator"
          />
        ),
      },
      [TransactionSide.Sent]: {
        designation: t('common.validator', 'Validator'),
        icon: () => <Money />,
        header: () => (
          <Trans
            i18nKey="account.transaction.reclaimEscrow.sent"
            t={t}
            components={[amount]}
            defaults="Reclaimed <0></0> from validator"
          />
        ),
      },
    },
    [transactionTypes.TransactionType.StakingAmendCommissionSchedule]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.StakingAllow]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.StakingWithdraw]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.RoothashExecutorCommit]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.RoothashExecutorProposerTimeout]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.RegistryRegisterEntity]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.RegistryRegisterNode]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.RegistryRegisterRuntime]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.GovernanceCastVote]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.GovernanceSubmitProposal]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.BeaconPvssCommit]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.BeaconPvssReveal]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
    [transactionTypes.TransactionType.BeaconVrfProve]: {
      [TransactionSide.Received]: unrecognizedTransaction,
      [TransactionSide.Sent]: unrecognizedTransaction,
    },
  }

  const isTypeRecognized = (type: string | undefined): type is transactionTypes.TransactionType =>
    type ? type in transactionDictionary : false

  const matchingConfiguration = isTypeRecognized(transaction.type)
    ? transactionDictionary[transaction.type][side]
    : unrecognizedTransaction

  const icon = matchingConfiguration.icon()
  const header = matchingConfiguration.header()
  const designation = matchingConfiguration.designation
  const blockExplorerLink = config[props.network][backend()]?.blockExplorer

  return (
    <Card round="small" background="background-front" gap="none" elevation="xsmall">
      <CardHeader pad={{ horizontal: 'medium', vertical: 'small' }} gap="none" background="brand" wrap={true}>
        <Box direction="row" gap="small">
          {icon}
          <Text>{header}</Text>
        </Box>
      </CardHeader>
      <CardBody pad={{ horizontal: 'none', vertical: 'none' }}>
        <Grid columns={{ count: 'fit', size: 'xsmall' }} gap="none">
          <InfoBox icon={<Money color="brand" />} label={t('common.amount', 'Amount')} value={amount} />
          {otherAddress && (
            <NavLink data-testid="external-wallet-address" to={`/account/${otherAddress}`}>
              <InfoBox
                icon={<ContactInfo color="brand" />}
                label={designation}
                value={<ShortAddress address={otherAddress} />}
              />
            </NavLink>
          )}
          {!otherAddress && (
            <InfoBox
              icon={<ContactInfo color="brand" />}
              label={designation}
              value={t('common.unavailable', 'Unavailable')}
            />
          )}
          <InfoBox
            icon={<Cube color="brand" />}
            label={t('common.block', 'Block')}
            value={transaction.level}
          />
        </Grid>
      </CardBody>
      <CardFooter background="background-contrast" pad={{ horizontal: 'medium' }}>
        <Text size="small">
          <DateFormatter date={transaction.timestamp!} />
        </Text>
        <Box direction="row">
          <Button
            icon={<CircleInformation color="dark-3" />}
            hoverIndicator
            href={blockExplorerLink.replace('{{txHash}}', encodeURIComponent(transaction.hash))}
            target="_blank"
            rel="noopener"
            data-testid="explorer-link"
          />
        </Box>
      </CardFooter>
    </Card>
  )
}
Example #26
Source File: APIReview.tsx    From one-platform with MIT License 4 votes vote down vote up
APIReview = (): JSX.Element => {
  const { getValues } = useFormContext<FormData>();
  const [formData] = useState(getValues());

  return (
    <Stack hasGutter>
      <StackItem>
        <Card>
          <CardTitle>
            <Title headingLevel="h2">API Details</Title>
          </CardTitle>
          <CardBody>
            <Stack hasGutter>
              <StackItem>
                <Description title="Name" isRequired>
                  <Title headingLevel="h6">{formData.name}</Title>
                </Description>
              </StackItem>
              <StackItem>
                <Description title="Description" isRequired>
                  <Title headingLevel="h6">
                    <ReadMore>{formData.description}</ReadMore>
                  </Title>
                </Description>
              </StackItem>
              <StackItem>
                <Description title="Owners">
                  <Title headingLevel="h6">
                    <List isPlain isBordered>
                      {formData.owners.map(({ email }) => (
                        <ListItem key={email}>{email}</ListItem>
                      ))}
                    </List>
                  </Title>
                </Description>
              </StackItem>
            </Stack>
          </CardBody>
        </Card>
      </StackItem>
      {formData.schemas?.map((schema, schemaIndex) => (
        <StackItem key={schema.name}>
          <Card>
            <CardTitle>
              <Title headingLevel="h2">{`API Schema #${schemaIndex + 1}`}</Title>
            </CardTitle>
            <CardBody>
              <Stack hasGutter>
                <StackItem>
                  <Description title="Schema Name" isRequired>
                    <Title headingLevel="h6">{schema.name}</Title>
                  </Description>
                </StackItem>
                <StackItem>
                  <Description title="Description" isRequired>
                    <Title headingLevel="h6">
                      <ReadMore>{schema.description}</ReadMore>
                    </Title>
                  </Description>
                </StackItem>
                <StackItem>
                  <Description title="Type" isRequired>
                    <CatalogBigButton
                      title={apiOptions[schema.category].title}
                      desc={apiOptions[schema.category].desc}
                      image={`${config.baseURL}/images/${apiOptions[schema.category].image}`}
                    />
                  </Description>
                </StackItem>
                <StackItem>
                  <Split hasGutter>
                    <SplitItem isFilled>
                      <Description title="App URL" isRequired>
                        <Title headingLevel="h6">{schema.appURL}</Title>
                      </Description>
                    </SplitItem>
                    <SplitItem isFilled>
                      <Description title="App Documentation URL" isRequired>
                        <Title headingLevel="h6">{schema.docURL}</Title>
                      </Description>
                    </SplitItem>
                  </Split>
                </StackItem>
                <StackItem>
                  <Description title="Environments">
                    {schema.environments.map((env, envIndex) => (
                      <div
                        style={{ border: '1px solid #d2d2d2' }}
                        className="pf-u-p-lg pf-u-mb-md"
                        key={`schema-${schemaIndex + 1}-env-${envIndex + 1}`}
                      >
                        <Grid hasGutter>
                          <GridItem span={6}>
                            <Description title="Name" isRequired>
                              <Title headingLevel="h6" className="uppercase">
                                {env.name}
                              </Title>
                            </Description>
                          </GridItem>
                          <GridItem span={6}>
                            <Description title="API Base Path" isRequired>
                              <Title headingLevel="h6">{env.apiBasePath}</Title>
                            </Description>
                          </GridItem>
                          <GridItem span={6}>
                            <Description title="API Schema Endpoint" isRequired>
                              <Title headingLevel="h6">{env.schemaEndpoint}</Title>
                            </Description>
                          </GridItem>
                          <GridItem span={12}>
                            <Description title="Headers" isRequired>
                              <DataList aria-label="header-list" isCompact>
                                {env.headers?.map(
                                  ({ key }, index) =>
                                    key && (
                                      <DataListItem key={`${key}-${index + 1}`}>
                                        <DataListItemRow>
                                          <DataListItemCells
                                            dataListCells={[
                                              <DataListCell key={`${key}-${index + 1}-cell-1`}>
                                                <span>{key}</span>
                                              </DataListCell>,
                                              <DataListCell key={`${key}-${index + 1}-cell-2`}>
                                                <span>********</span>
                                              </DataListCell>,
                                            ]}
                                          />
                                        </DataListItemRow>
                                      </DataListItem>
                                    )
                                )}
                              </DataList>
                            </Description>
                          </GridItem>
                          <GridItem span={12}>
                            <Checkbox
                              isChecked={env.isPublic}
                              isDisabled
                              id={`schema-${schemaIndex}-env-${envIndex}-isPublic`}
                              label="Is this API accessible from public?"
                            />
                          </GridItem>
                        </Grid>
                      </div>
                    ))}
                  </Description>
                </StackItem>
              </Stack>
            </CardBody>
          </Card>
        </StackItem>
      ))}
    </Stack>
  );
}
Example #27
Source File: Sidebar.tsx    From one-platform with MIT License 4 votes vote down vote up
Sidebar = ({ isOpen }: Props): JSX.Element => {
  const { pathname, search } = useLocation();
  const url = pathname.replace(config.baseURL, '');
  const { logs, quickLink } = useRecentVisit();
  const userInfo = opcBase.auth?.getUserInfo();
  const navLinks = useMemo(() => navMenuLinks(userInfo?.rhatUUID || ''), [userInfo?.rhatUUID]);

  const getToolImage = useCallback((tool: string) => {
    if (tool === 'playground') return `${config.baseURL}/images/gql-playground-logo.svg`;
    if (tool === 'redoc') return `${config.baseURL}/images/redoc-logo.png`;
    return `${config.baseURL}/images/${tool}-logo.svg`;
  }, []);

  const isQuickLinkActive = logs?.[0]?.url === pathname;
  const isQuickLinkGraphql = logs?.[0]?.tool !== 'swagger' && logs?.[0]?.tool !== 'redoc';

  const getQuickLinks = useCallback(() => {
    if (isQuickLinkActive) {
      const links = isQuickLinkGraphql ? graphqlQuickLinks : restQuickLinks;
      return (
        <NavGroup title={`${quickLink?.name} quick links`}>
          {links.map(({ url: path, label, img }) => (
            <NavItem key={`quick-link-${label}`}>
              <Link
                to={path
                  .replace(':id', quickLink?.id || '')
                  .replace(':envSlug', quickLink?.envSlug || '')}
              >
                <Split>
                  <SplitItem className="pf-u-mr-sm pf-u-display-flex pf-u-align-items-center">
                    <img
                      src={`${config.baseURL}/images/${img}`}
                      width="16px"
                      alt="quick link icon"
                    />
                  </SplitItem>
                  <SplitItem>{label}</SplitItem>
                </Split>
              </Link>
            </NavItem>
          ))}
        </NavGroup>
      );
    }
    return null;
  }, [isQuickLinkActive, isQuickLinkGraphql, quickLink?.id, quickLink?.name, quickLink?.envSlug]);

  return (
    <PageSidebar
      theme="light"
      isNavOpen={isOpen}
      className={styles['app-layout--sidebar']}
      nav={
        <Stack hasGutter>
          <StackItem isFilled>
            <Nav theme="light">
              <NavList className="pf-u-mb-lg">
                {navLinks.map(({ url: pattern, label, icon: Icon }, index) => (
                  <NavItem
                    key={`nav-menu-link:${label}`}
                    id="home-link"
                    itemId={index}
                    isActive={pattern === url + search}
                  >
                    <Link to={pattern}>
                      <Split>
                        <SplitItem className="pf-u-mr-sm">
                          <Icon />
                        </SplitItem>
                        <SplitItem>{label}</SplitItem>
                      </Split>
                    </Link>
                  </NavItem>
                ))}
              </NavList>
              {getQuickLinks()}
              <NavGroup title="Recent visited">
                {logs?.map(({ title, url: toolUrl, tool }) => (
                  <NavItem key={`nav-item-${toolUrl}`}>
                    <Link to={toolUrl.replace(config.baseURL, '/')}>
                      <Split hasGutter className="pf-u-align-items-center">
                        <SplitItem>
                          <img src={getToolImage(tool)} style={{ height: '24px' }} alt="swagger" />
                        </SplitItem>
                        <SplitItem>
                          <Title headingLevel="h6">{title}</Title>
                          <Text component={TextVariants.small} className="capitalize">
                            {tool}
                          </Text>
                        </SplitItem>
                      </Split>
                    </Link>
                  </NavItem>
                ))}
              </NavGroup>
            </Nav>
          </StackItem>
          <StackItem>
            <Footer />
          </StackItem>
        </Stack>
      }
    />
  );
}