Orgs: Add skeleton loader (#79141)

add skeleton for orgs list
This commit is contained in:
Ashley Harrison 2023-12-06 16:07:32 +00:00 committed by GitHub
parent 74cb1423c1
commit 9ab8292949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 29 deletions

View File

@ -5,7 +5,7 @@ import { getBackendSrv, isFetchError } from '@grafana/runtime';
import { LinkButton } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import { contextSrv } from 'app/core/services/context_srv';
import { AccessControlAction } from 'app/types';
import { AccessControlAction, Organization } from 'app/types';
import { AdminOrgsTable } from './AdminOrgsTable';
@ -14,7 +14,7 @@ const deleteOrg = async (orgId: number) => {
};
const getOrgs = async () => {
return await getBackendSrv().get('/api/orgs');
return await getBackendSrv().get<Organization[]>('/api/orgs');
};
const getErrorMessage = (error: Error) => {
@ -30,17 +30,17 @@ export default function AdminListOrgsPages() {
}, [fetchOrgs]);
return (
<Page navId="global-orgs">
<Page.Contents>
<>
<div className="page-action-bar">
<div className="page-action-bar__spacer" />
<Page
navId="global-orgs"
actions={
<LinkButton icon="plus" href="org/new" disabled={!canCreateOrg}>
New org
</LinkButton>
</div>
}
>
<Page.Contents>
{state.error && getErrorMessage(state.error)}
{state.loading && 'Fetching organizations'}
{state.loading && <AdminOrgsTable.Skeleton />}
{state.value && (
<AdminOrgsTable
orgs={state.value}
@ -49,7 +49,6 @@ export default function AdminListOrgsPages() {
}}
/>
)}
</>
</Page.Contents>
</Page>
);

View File

@ -1,6 +1,9 @@
import { css } from '@emotion/css';
import React, { useState } from 'react';
import Skeleton from 'react-loading-skeleton';
import { Button, ConfirmModal } from '@grafana/ui';
import { GrafanaTheme2 } from '@grafana/data';
import { Button, ConfirmModal, useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { AccessControlAction, Organization } from 'app/types';
@ -9,12 +12,7 @@ interface Props {
onDelete: (orgId: number) => void;
}
export function AdminOrgsTable({ orgs, onDelete }: Props) {
const canDeleteOrgs = contextSrv.hasPermission(AccessControlAction.OrgsDelete);
const [deleteOrg, setDeleteOrg] = useState<Organization>();
return (
<table className="filter-table form-inline filter-table--hover">
const getTableHeader = () => (
<thead>
<tr>
<th>ID</th>
@ -22,6 +20,15 @@ export function AdminOrgsTable({ orgs, onDelete }: Props) {
<th style={{ width: '1%' }}></th>
</tr>
</thead>
);
export function AdminOrgsTable({ orgs, onDelete }: Props) {
const canDeleteOrgs = contextSrv.hasPermission(AccessControlAction.OrgsDelete);
const [deleteOrg, setDeleteOrg] = useState<Organization>();
return (
<table className="filter-table form-inline filter-table--hover">
{getTableHeader()}
<tbody>
{orgs.map((org) => (
<tr key={`${org.id}-${org.name}`}>
@ -66,3 +73,38 @@ export function AdminOrgsTable({ orgs, onDelete }: Props) {
</table>
);
}
const AdminOrgsTableSkeleton = () => {
const styles = useStyles2(getSkeletonStyles);
return (
<table className="filter-table">
{getTableHeader()}
<tbody>
{new Array(3).fill(null).map((_, index) => (
<tr key={index}>
<td>
<Skeleton width={16} />
</td>
<td>
<Skeleton width={240} />
</td>
<td>
<Skeleton containerClassName={styles.deleteButton} width={22} height={24} />
</td>
</tr>
))}
</tbody>
</table>
);
};
AdminOrgsTable.Skeleton = AdminOrgsTableSkeleton;
const getSkeletonStyles = (theme: GrafanaTheme2) => ({
deleteButton: css({
alignItems: 'center',
display: 'flex',
height: 30,
lineHeight: 1,
}),
});