Files
backroad/app/portainer/home/EnvironmentList/EnvironmentList.tsx
Chaim Lev-Ari 0f3c7b1424 refactor(home): migrate view to react [EE-1810] (#6314)
* refactor(http): parse axios errors (#6325)

* refactor(home): use endpoint-list as react component [EE-1814] (#6060)

* refactor(home): use endpoint-list as react component

fix(home): add missing features and refactors

- kubebutton
- group name
- poll when endpoint is off
- state management

refactor(endpoints): use stat component

fix(endpoints): add space between items

refactor(endpoints): move stats to components

refactor(endpoints): fetch time

refactor(home): move logic

refactor(home): move fe render logic

refactor(settings): use vanilla js for publicSettings

refactor(kube): remove angular from kube config service

feat(home): add kubeconfig button

feat(home): send analytics when opening kubeconfig modal

fix(home): memoize footer

refactor(home): use react-query for loading

fix(home): show correct control for kubeconfig modal

refactor(home): use debounce

refactor(home): use new components

refactor(home): replace endpoints with environments

refactor(home): move endpoint-list component to home

fix(home): show group name

refactor(home): use switch for environment icon

fix(kubeconfig): fix default case

refactor(axios): use parse axios error

refactor(home): use link components for navigate

fix(home): align azure icon

refactor(home): refactor stats

refactor(home): export envstatusbadge

refactor(home): remove unused bindings

* chore(home): write tests for edge indicator

* chore(home): basic stories for environment item

* style(settings): reformat

* fix(environments): add publicurl

* refactor(home): use table components

* refactor(datatables): merge useSearchBarState

* refactor(home): fetch group in env item

* chore(tests): basic tests

* chore(home): test when no envs

* refactor(tags): use axios for tagService

* refactor(env-groups): use axios for getGroups

* feat(app): ui-state context provider

* refactor(home): create MotdPanel

* refactor(app): create InformationPanel

* feat(endpoints): fetch number of total endpoints

* refactor(app): merge hooks

* refactor(home): migrate view to react [EE-1810]

fixes [EE-1810]

refactor(home): wip use react view

feat(home): show message if no endpoints

refactor(home): show endpoint list

refactor(home): don't use home to manage link

refactor(home): move state

refactor(home): check if edge using util

refactor(home): move inf panels

chore(home): tests

refactor(home): load groups and tags in env-item

refactor(settings): revert publicSettings change

refactor(home): move confirm snapshot method

* fix(home): show tags

* fix(environments): handle missing snapshots

* fix(kube/volumes): fetch pesistent volume claims

* refactor(kube): remove use of endpointProvider

* refactor(endpoints): set current endpoint

* chore(home): add data-cy for tests

* chore(tests): mock axios-progress-bar

* refactor(home): move use env list to env module

* feat(app): sync home view changes with ee

* fix(home): sort page header

* fix(app): fix tests

* chore(github): use yarn cache

* refactor(environments): load list of groups

* chore(babel): remove auto 18n keys extraction

* chore(environments): fix tests

* refactor(k8s/application): use current endpoint

* fix(app/header): add margin to header

* refactor(app): remove unused types

* refactor(app): use rq onError handler

* refactor(home): wrap element with button
2022-03-08 14:14:23 +02:00

160 lines
4.7 KiB
TypeScript

import { ReactNode, useEffect, useState } from 'react';
import clsx from 'clsx';
import { PaginationControls } from '@/portainer/components/pagination-controls';
import { usePaginationLimitState } from '@/portainer/hooks/usePaginationLimitState';
import { Environment } from '@/portainer/environments/types';
import { Button } from '@/portainer/components/Button';
import { useIsAdmin } from '@/portainer/hooks/useUser';
import {
SearchBar,
useSearchBarState,
} from '@/portainer/components/datatables/components/SearchBar';
import {
TableActions,
TableContainer,
TableTitle,
} from '@/portainer/components/datatables/components';
import { TableFooter } from '@/portainer/components/datatables/components/TableFooter';
import { useDebounce } from '@/portainer/hooks/useDebounce';
import { useEnvironmentList } from '@/portainer/environments/queries';
import { useGroups } from '@/portainer/environment-groups/queries';
import { EnvironmentItem } from './EnvironmentItem';
import { KubeconfigButton } from './KubeconfigButton';
import styles from './EnvironmentList.module.css';
import { NoEnvironmentsInfoPanel } from './NoEnvironmentsInfoPanel';
interface Props {
onClickItem(environment: Environment): void;
onRefresh(): void;
}
export function EnvironmentList({ onClickItem, onRefresh }: Props) {
const homepageLoadTime = usePageLoadingTime();
const isAdmin = useIsAdmin();
const storageKey = 'home_endpoints';
const [searchBarValue, setSearchBarValue] = useSearchBarState(storageKey);
const [pageLimit, setPageLimit] = usePaginationLimitState(storageKey);
const [page, setPage] = useState(1);
const debouncedTextFilter = useDebounce(searchBarValue);
useEffect(() => {
setPage(1);
}, [searchBarValue]);
const groupsQuery = useGroups();
const { isLoading, environments, totalCount, totalAvailable } =
useEnvironmentList(page, pageLimit, debouncedTextFilter, true);
return (
<>
{totalAvailable === 0 && <NoEnvironmentsInfoPanel isAdmin={isAdmin} />}
<div className="row">
<div className="col-sm-12">
<TableContainer>
<TableTitle icon="fa-plug" label="Environments" />
<TableActions className={styles.actionBar}>
<div className={styles.description}>
<i className="fa fa-exclamation-circle blue-icon space-right" />
Click on an environment to manage
</div>
{isAdmin && (
<Button
onClick={onRefresh}
data-cy="home-refreshEndpointsButton"
className={clsx(styles.refreshEnvironmentsButton)}
>
<i className="fa fa-sync space-right" aria-hidden="true" />
Refresh
</Button>
)}
<KubeconfigButton environments={environments} />
</TableActions>
<SearchBar
value={searchBarValue}
onChange={setSearchBarValue}
placeholder="Search by name, group, tag, status, URL..."
data-cy="home-endpointsSearchInput"
/>
<div className="blocklist" data-cy="home-endpointList">
{renderItems(
isLoading,
totalCount,
environments.map((env) => (
<EnvironmentItem
key={env.Id}
environment={env}
groupName={
groupsQuery.data?.find((g) => g.Id === env.GroupId)?.Name
}
onClick={onClickItem}
homepageLoadTime={homepageLoadTime}
/>
))
)}
</div>
<TableFooter>
<PaginationControls
showAll={totalCount <= 100}
pageLimit={pageLimit}
page={page}
onPageChange={setPage}
totalCount={totalCount}
onPageLimitChange={setPageLimit}
/>
</TableFooter>
</TableContainer>
</div>
</div>
</>
);
}
function renderItems(
isLoading: boolean,
totalCount: number,
items: ReactNode
) {
if (isLoading) {
return (
<div className="text-center text-muted" data-cy="home-loadingEndpoints">
Loading...
</div>
);
}
if (!totalCount) {
return (
<div className="text-center text-muted" data-cy="home-noEndpoints">
No environments available.
</div>
);
}
return items;
}
function usePageLoadingTime() {
const [homepageLoadTime, setHomepageLoadTime] = useState<
number | undefined
>();
useEffect(() => {
setHomepageLoadTime(Math.floor(Date.now() / 1000));
}, []);
return homepageLoadTime;
}