Skip to content

Commit

Permalink
[frontend] add skeleton loader on main lists (#1409)
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaumejparis authored Nov 12, 2024
1 parent e396e68 commit 950e6ab
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 148 deletions.
5 changes: 4 additions & 1 deletion openbas-front/src/admin/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,19 @@ const Dashboard = () => {
const dispatch = useAppDispatch();

// Exercises
const [loadingExercises, setLoadingExercises] = useState(true);
const [exercises, setExercises] = useState<EndpointStore[]>([]);
const searchPaginationInput = {
sorts: initSorting('exercise_updated_at', 'DESC'),
page: 0,
size: 6,
};
useEffect(() => {
setLoadingExercises(true);
searchExercises(searchPaginationInput).then((result: { data: Page<ExerciseSimple> }) => {
const { data } = result;
setExercises(data.content);
});
}).finally(() => setLoadingExercises(false));
}, []);

// Statistics
Expand Down Expand Up @@ -278,6 +280,7 @@ const Dashboard = () => {
exercises={exercises}
hasHeader={false}
variant="reduced-view"
loading={loadingExercises}
/>
</Paper>
</Grid>
Expand Down
115 changes: 64 additions & 51 deletions openbas-front/src/admin/components/atomic_testings/InjectDtoList.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { HelpOutlineOutlined } from '@mui/icons-material';
import { List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { CSSProperties, FunctionComponent, useMemo, useState } from 'react';
Expand All @@ -12,6 +13,7 @@ import Empty from '../../../components/Empty';
import { useFormatter } from '../../../components/i18n';
import ItemStatus from '../../../components/ItemStatus';
import ItemTargets from '../../../components/ItemTargets';
import PaginatedListLoader from '../../../components/PaginatedListLoader';
import type { InjectResultDTO, SearchPaginationInput } from '../../../utils/api-types';
import { isNotEmptyField } from '../../../utils/utils';
import InjectIcon from '../common/injects/InjectIcon';
Expand Down Expand Up @@ -82,6 +84,8 @@ const InjectDtoList: FunctionComponent<Props> = ({
const classes = useStyles();
const { t, fldt, tPick, nsdt } = useFormatter();

const [loading, setLoading] = useState<boolean>(true);

// Filter and sort hook
const [injects, setInjects] = useState<InjectResultDTO[]>([]);

Expand Down Expand Up @@ -152,10 +156,15 @@ const InjectDtoList: FunctionComponent<Props> = ({
},
], []);

const search = (input: SearchPaginationInput) => {
setLoading(true);
return fetchInjects(input).finally(() => setLoading(false));
};

return (
<>
<PaginationComponentV2
fetch={fetchInjects}
fetch={search}
searchPaginationInput={searchPaginationInput}
setContent={setInjects}
entityPrefix="inject"
Expand All @@ -180,57 +189,61 @@ const InjectDtoList: FunctionComponent<Props> = ({
)}
/>
</ListItem>
{injects.map((injectDto) => {
return (
<ListItem
key={injectDto.inject_id}
classes={{ root: classes.item }}
divider
secondaryAction={(
<AtomicTestingPopover
atomic={injectDto}
actions={['Duplicate', 'Delete']}
onDelete={result => setInjects(injects.filter(e => (e.inject_id !== result)))}
inList
/>
)}
disablePadding
>
<ListItemButton
component={Link}
to={goTo(injectDto.inject_id)}
>
<ListItemIcon>
<InjectIcon
isPayload={isNotEmptyField(injectDto.inject_injector_contract?.injector_contract_payload)}
type={
injectDto.inject_injector_contract?.injector_contract_payload
? injectDto.inject_injector_contract.injector_contract_payload.payload_collector_type
|| injectDto.inject_injector_contract.injector_contract_payload.payload_type
: injectDto.inject_type
}
variant="list"
/>
</ListItemIcon>
<ListItemText
primary={(
<div className={classes.bodyItems}>
{headers.map(header => (
<div
key={header.field}
className={classes.bodyItem}
style={inlineStyles[header.field]}
>
{header.value?.(injectDto)}
</div>
))}
</div>
{
loading
? <PaginatedListLoader Icon={HelpOutlineOutlined} headers={headers} headerStyles={inlineStyles} />
: injects.map((injectDto, index) => {
return (
<ListItem
key={injectDto.inject_id}
classes={{ root: classes.item }}
divider={injects.length !== index + 1}
secondaryAction={(
<AtomicTestingPopover
atomic={injectDto}
actions={['Duplicate', 'Delete']}
onDelete={result => setInjects(injects.filter(e => (e.inject_id !== result)))}
inList
/>
)}
/>
</ListItemButton>
</ListItem>
);
})}
disablePadding
>
<ListItemButton
component={Link}
to={goTo(injectDto.inject_id)}
>
<ListItemIcon>
<InjectIcon
isPayload={isNotEmptyField(injectDto.inject_injector_contract?.injector_contract_payload)}
type={
injectDto.inject_injector_contract?.injector_contract_payload
? injectDto.inject_injector_contract.injector_contract_payload.payload_collector_type
|| injectDto.inject_injector_contract.injector_contract_payload.payload_type
: injectDto.inject_type
}
variant="list"
/>
</ListItemIcon>
<ListItemText
primary={(
<div className={classes.bodyItems}>
{headers.map(header => (
<div
key={header.field}
className={classes.bodyItem}
style={inlineStyles[header.field]}
>
{header.value?.(injectDto)}
</div>
))}
</div>
)}
/>
</ListItemButton>
</ListItem>
);
})
}
{!injects ? (<Empty message={t('No data available')} />) : null}
</List>
</>
Expand Down
98 changes: 55 additions & 43 deletions openbas-front/src/admin/components/scenarios/Scenarios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import { useFormatter } from '../../../components/i18n';
import ItemCategory from '../../../components/ItemCategory';
import ItemSeverity from '../../../components/ItemSeverity';
import ItemTags from '../../../components/ItemTags';
import PaginatedListLoader from '../../../components/PaginatedListLoader';
import PlatformIcon from '../../../components/PlatformIcon';
import { useHelper } from '../../../store';
import type { FilterGroup } from '../../../utils/api-types';
import type { FilterGroup, SearchPaginationInput } from '../../../utils/api-types';
import ImportUploaderScenario from './ImportUploaderScenario';
import ScenarioPopover from './scenario/ScenarioPopover';
import ScenarioStatus from './scenario/ScenarioStatus';
Expand Down Expand Up @@ -77,6 +78,8 @@ const Scenarios = () => {
const classes = useStyles();
const { t, nsdt } = useFormatter();

const [loading, setLoading] = useState<boolean>(true);

// Fetching data
const { userAdmin } = useHelper((helper: TagHelper & UserHelper) => ({
userAdmin: helper.getMe()?.user_admin ?? false,
Expand Down Expand Up @@ -192,11 +195,16 @@ const Scenarios = () => {
exportFileName: `${t('Scenarios')}.csv`,
};

const search = (input: SearchPaginationInput) => {
setLoading(true);
return searchScenarios(input).finally(() => setLoading(false));
};

return (
<>
<Breadcrumbs variant="list" elements={[{ label: t('Scenarios'), current: true }]} />
<PaginationComponentV2
fetch={searchScenarios}
fetch={search}
searchPaginationInput={searchPaginationInput}
setContent={setScenarios}
entityPrefix="scenario"
Expand Down Expand Up @@ -227,48 +235,52 @@ const Scenarios = () => {
)}
/>
</ListItem>
{scenarios.map((scenario: ScenarioStore) => {
return (
<ListItem
key={scenario.scenario_id}
divider
secondaryAction={(
<ScenarioPopover
scenario={scenario}
actions={['Duplicate', 'Export', 'Delete']}
onDelete={result => setScenarios(scenarios.filter(e => (e.scenario_id !== result)))}
inList
/>
)}
disablePadding
>
<ListItemButton
component={Link}
to={`/admin/scenarios/${scenario.scenario_id}`}
classes={{ root: classes.item }}
>
<ListItemIcon>
<MovieFilterOutlined color="primary" />
</ListItemIcon>
<ListItemText
primary={(
<div className={classes.bodyItems}>
{headers.map(header => (
<div
key={header.field}
className={classes.bodyItem}
style={inlineStyles[header.field]}
>
{header.value(scenario)}
</div>
))}
</div>
{
loading
? <PaginatedListLoader Icon={MovieFilterOutlined} headers={headers} headerStyles={inlineStyles} />
: scenarios.map((scenario: ScenarioStore, index) => {
return (
<ListItem
key={scenario.scenario_id}
divider={scenarios.length !== index + 1}
secondaryAction={(
<ScenarioPopover
scenario={scenario}
actions={['Duplicate', 'Export', 'Delete']}
onDelete={result => setScenarios(scenarios.filter(e => (e.scenario_id !== result)))}
inList
/>
)}
/>
</ListItemButton>
</ListItem>
);
})}
disablePadding
>
<ListItemButton
component={Link}
to={`/admin/scenarios/${scenario.scenario_id}`}
classes={{ root: classes.item }}
>
<ListItemIcon>
<MovieFilterOutlined color="primary" />
</ListItemIcon>
<ListItemText
primary={(
<div className={classes.bodyItems}>
{headers.map(header => (
<div
key={header.field}
className={classes.bodyItem}
style={inlineStyles[header.field]}
>
{header.value(scenario)}
</div>
))}
</div>
)}
/>
</ListItemButton>
</ListItem>
);
})
}
</List>
{userAdmin && (
<ScenarioCreation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ const Scenario = ({ setOpenInstantiateSimulationAndStart }: { setOpenInstantiate
exercises={exercises}
queryableHelpers={queryableHelpers}
secondaryAction={secondaryAction}
loading={loadingScenarioExercises}
/>
</Paper>
</Grid>
Expand Down
Loading

0 comments on commit 950e6ab

Please sign in to comment.