2018-03-16 21:49:10 +02:00
|
|
|
import React, { Fragment } from 'react';
|
2017-11-23 14:18:38 +02:00
|
|
|
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
|
|
|
|
import remcalc from 'remcalc';
|
2018-02-01 12:38:12 +02:00
|
|
|
import styled from 'styled-components';
|
2017-10-31 12:29:15 +02:00
|
|
|
import titleCase from 'title-case';
|
2017-11-23 14:18:38 +02:00
|
|
|
import { Link } from 'react-router-dom';
|
|
|
|
import { Field } from 'redux-form';
|
2018-02-23 17:59:12 +02:00
|
|
|
import Flex from 'styled-flex-component';
|
2018-03-16 21:49:10 +02:00
|
|
|
import queryString from 'query-string';
|
2017-09-20 12:30:53 +03:00
|
|
|
|
2017-09-27 17:44:57 +03:00
|
|
|
import {
|
2017-11-23 14:18:38 +02:00
|
|
|
Anchor,
|
2017-09-27 17:44:57 +03:00
|
|
|
FormGroup,
|
2017-11-23 14:18:38 +02:00
|
|
|
Checkbox,
|
2017-10-31 12:29:15 +02:00
|
|
|
Table,
|
|
|
|
TableThead,
|
|
|
|
TableTr,
|
|
|
|
TableTh,
|
|
|
|
TableTd,
|
2017-11-23 14:18:38 +02:00
|
|
|
TableTbody,
|
2018-03-16 21:49:10 +02:00
|
|
|
PaginationTableFoot,
|
|
|
|
PaginationItem,
|
2017-11-23 14:18:38 +02:00
|
|
|
StatusLoader,
|
|
|
|
Popover,
|
2017-10-31 12:29:15 +02:00
|
|
|
PopoverContainer,
|
|
|
|
PopoverTarget,
|
|
|
|
PopoverItem,
|
|
|
|
PopoverDivider,
|
2017-11-23 14:18:38 +02:00
|
|
|
DotIcon,
|
2018-02-01 17:33:58 +02:00
|
|
|
ActionsIcon
|
2017-09-27 17:44:57 +03:00
|
|
|
} from 'joyent-ui-toolkit';
|
|
|
|
|
2018-03-16 21:49:10 +02:00
|
|
|
import GLOBAL, { Global } from '@state/global';
|
2018-03-06 03:14:33 +02:00
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const stateColor = {
|
|
|
|
PROVISIONING: 'primary',
|
|
|
|
RUNNING: 'green',
|
|
|
|
STOPPING: 'grey',
|
|
|
|
STOPPED: 'grey',
|
|
|
|
DELETED: 'secondaryActive',
|
|
|
|
FAILED: 'red'
|
|
|
|
};
|
|
|
|
|
2018-02-01 12:38:12 +02:00
|
|
|
const A = styled(Anchor)`
|
|
|
|
color: ${props => props.theme.text};
|
|
|
|
text-decoration: none;
|
2018-02-01 17:33:58 +02:00
|
|
|
font-weight: ${props => props.theme.font.weight.semibold};
|
2018-03-21 19:35:51 +02:00
|
|
|
white-space: nowrap;
|
|
|
|
overflow: hidden;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
max-width: 100%;
|
|
|
|
display: block;
|
2018-02-01 17:33:58 +02:00
|
|
|
`;
|
2018-02-01 12:38:12 +02:00
|
|
|
|
2018-02-15 16:37:59 +02:00
|
|
|
const ItemAnchor = styled(Anchor)`
|
|
|
|
color: ${props => props.theme.text};
|
|
|
|
-webkit-text-fill-color: currentcolor;
|
|
|
|
text-decoration: none;
|
|
|
|
`;
|
|
|
|
|
2018-02-23 17:59:12 +02:00
|
|
|
const Actions = styled(Flex)`
|
|
|
|
height: ${remcalc(48)};
|
|
|
|
`;
|
|
|
|
|
2018-03-16 21:49:10 +02:00
|
|
|
export const FetchingItem = () => (
|
|
|
|
<TableTr colSpan="6">
|
|
|
|
<TableTd colSpan="6" middle center>
|
|
|
|
<StatusLoader />
|
|
|
|
</TableTd>
|
|
|
|
</TableTr>
|
|
|
|
);
|
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
export const Item = ({
|
|
|
|
id = '',
|
2017-10-31 12:29:15 +02:00
|
|
|
name,
|
2017-12-21 02:12:42 +02:00
|
|
|
state = 'RUNNING',
|
2017-11-23 14:18:38 +02:00
|
|
|
created,
|
|
|
|
allowedActions = {},
|
2017-12-21 02:12:42 +02:00
|
|
|
mutating = false,
|
2018-02-15 16:37:59 +02:00
|
|
|
onCreateImage,
|
2017-10-31 12:29:15 +02:00
|
|
|
onStart,
|
2017-11-23 14:18:38 +02:00
|
|
|
onStop,
|
2017-10-31 12:29:15 +02:00
|
|
|
onReboot,
|
2018-02-23 17:59:12 +02:00
|
|
|
onRemove,
|
|
|
|
onClick
|
2017-10-31 12:29:15 +02:00
|
|
|
}) => (
|
|
|
|
<TableTr>
|
2017-11-23 14:18:38 +02:00
|
|
|
<TableTd padding="0" paddingLeft={remcalc(12)} middle left>
|
2017-11-23 14:18:38 +02:00
|
|
|
<FormGroup name={id} paddingTop={remcalc(4)} field={Field}>
|
|
|
|
<Checkbox noMargin />
|
2017-10-31 12:29:15 +02:00
|
|
|
</FormGroup>
|
|
|
|
</TableTd>
|
2017-11-23 14:18:38 +02:00
|
|
|
<TableTd middle left>
|
2018-03-21 19:35:51 +02:00
|
|
|
<A to={`/instances/${id}`} component={Link}>
|
2017-11-23 14:18:38 +02:00
|
|
|
{name}
|
2018-02-01 17:33:58 +02:00
|
|
|
</A>
|
2017-10-31 12:29:15 +02:00
|
|
|
</TableTd>
|
2017-11-23 14:18:38 +02:00
|
|
|
<TableTd middle left>
|
2017-12-21 02:12:42 +02:00
|
|
|
{mutating ? (
|
|
|
|
<StatusLoader small />
|
|
|
|
) : (
|
|
|
|
<span>
|
2018-02-01 17:33:58 +02:00
|
|
|
<DotIcon size={remcalc(12)} color={stateColor[state]} />{' '}
|
2017-12-21 02:12:42 +02:00
|
|
|
{titleCase(state)}
|
|
|
|
</span>
|
|
|
|
)}
|
2017-11-23 14:18:38 +02:00
|
|
|
</TableTd>
|
2017-12-04 20:24:24 +02:00
|
|
|
<TableTd xs="0" sm="160" middle left>
|
2017-11-23 14:18:38 +02:00
|
|
|
{distanceInWordsToNow(created)}
|
|
|
|
</TableTd>
|
2017-12-06 12:34:15 +02:00
|
|
|
<TableTd xs="0" sm="130" middle left>
|
2017-11-23 14:18:38 +02:00
|
|
|
<code>{id.substring(0, 7)}</code>
|
2017-10-31 12:29:15 +02:00
|
|
|
</TableTd>
|
2017-12-21 02:12:42 +02:00
|
|
|
{!mutating ? (
|
|
|
|
<PopoverContainer clickable>
|
|
|
|
<TableTd padding="0" hasBorder="left">
|
|
|
|
<PopoverTarget box>
|
2018-02-23 17:59:12 +02:00
|
|
|
<Actions alignCenter justifyCenter>
|
|
|
|
<ActionsIcon />
|
|
|
|
</Actions>
|
2017-12-21 02:12:42 +02:00
|
|
|
</PopoverTarget>
|
2018-01-08 17:13:05 +02:00
|
|
|
<Popover placement="bottom">
|
2017-12-21 02:12:42 +02:00
|
|
|
<PopoverItem disabled={!allowedActions.start} onClick={onStart}>
|
|
|
|
Start
|
|
|
|
</PopoverItem>
|
|
|
|
<PopoverItem disabled={!allowedActions.stop} onClick={onStop}>
|
|
|
|
Stop
|
|
|
|
</PopoverItem>
|
2018-01-04 13:05:22 +02:00
|
|
|
<PopoverItem disabled={!allowedActions.reboot} onClick={onReboot}>
|
|
|
|
Reboot
|
|
|
|
</PopoverItem>
|
2017-12-21 02:12:42 +02:00
|
|
|
<PopoverDivider />
|
2018-02-15 16:37:59 +02:00
|
|
|
<PopoverItem disabled={false} onClick={onCreateImage}>
|
|
|
|
<ItemAnchor
|
2018-03-21 19:35:51 +02:00
|
|
|
href={`${GLOBAL.origin}/images/~create/${id}`}
|
2018-02-15 16:37:59 +02:00
|
|
|
target="__blank"
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
>
|
|
|
|
Create Image
|
|
|
|
</ItemAnchor>
|
|
|
|
</PopoverItem>
|
|
|
|
<PopoverDivider />
|
2018-01-04 13:05:22 +02:00
|
|
|
<PopoverItem disabled={!allowedActions.remove} onClick={onRemove}>
|
|
|
|
Remove
|
|
|
|
</PopoverItem>
|
2017-12-21 02:12:42 +02:00
|
|
|
</Popover>
|
|
|
|
</TableTd>
|
|
|
|
</PopoverContainer>
|
|
|
|
) : (
|
|
|
|
<TableTd padding="0" hasBorder="left" center middle>
|
|
|
|
<ActionsIcon disabled />
|
2017-11-23 14:18:38 +02:00
|
|
|
</TableTd>
|
2017-12-21 02:12:42 +02:00
|
|
|
)}
|
2017-10-31 12:29:15 +02:00
|
|
|
</TableTr>
|
|
|
|
);
|
2017-10-13 22:51:18 +03:00
|
|
|
|
2017-09-27 17:44:57 +03:00
|
|
|
export default ({
|
2017-11-23 14:18:38 +02:00
|
|
|
sortBy = 'name',
|
|
|
|
sortOrder = 'desc',
|
2017-10-13 22:51:18 +03:00
|
|
|
submitting = false,
|
2017-11-23 14:18:38 +02:00
|
|
|
allSelected = false,
|
|
|
|
toggleSelectAll = () => null,
|
2017-12-21 02:12:42 +02:00
|
|
|
onSortBy = () => null,
|
2018-02-23 17:59:12 +02:00
|
|
|
children,
|
2018-03-16 21:49:10 +02:00
|
|
|
noInstances,
|
|
|
|
limit = 0,
|
|
|
|
offset = 0,
|
|
|
|
total = 0
|
|
|
|
}) => {
|
|
|
|
const numPages = Math.ceil(total / limit);
|
|
|
|
const currPage = Math.ceil((offset + limit) / limit);
|
|
|
|
const isLast = currPage === numPages;
|
|
|
|
const isFirst = currPage === 1;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<form>
|
|
|
|
<Table>
|
|
|
|
<TableThead>
|
|
|
|
<TableTr>
|
|
|
|
<TableTh xs="32" padding="0" paddingLeft={remcalc(12)} middle left>
|
|
|
|
<FormGroup paddingTop={remcalc(4)}>
|
|
|
|
<Checkbox
|
|
|
|
checked={allSelected}
|
|
|
|
disabled={submitting || noInstances}
|
|
|
|
onChange={toggleSelectAll}
|
|
|
|
noMargin
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh
|
|
|
|
onClick={() => onSortBy('name')}
|
|
|
|
sortOrder={sortOrder}
|
|
|
|
showSort={sortBy === 'name'}
|
|
|
|
left
|
|
|
|
middle
|
|
|
|
actionable
|
|
|
|
>
|
|
|
|
<span>Name </span>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh
|
|
|
|
xs="150"
|
|
|
|
onClick={() => onSortBy('state')}
|
|
|
|
sortOrder={sortOrder}
|
|
|
|
showSort={sortBy === 'state'}
|
|
|
|
left
|
|
|
|
middle
|
|
|
|
actionable
|
|
|
|
>
|
|
|
|
<span>Status </span>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh
|
|
|
|
xs="0"
|
|
|
|
sm="160"
|
|
|
|
onClick={() => onSortBy('created')}
|
|
|
|
sortOrder={sortOrder}
|
|
|
|
showSort={sortBy === 'created'}
|
|
|
|
left
|
|
|
|
middle
|
|
|
|
actionable
|
|
|
|
>
|
|
|
|
<span>Created </span>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh
|
|
|
|
xs="0"
|
|
|
|
sm="130"
|
|
|
|
onClick={() => onSortBy('id')}
|
|
|
|
sortOrder={sortOrder}
|
|
|
|
showSort={sortBy === 'id'}
|
|
|
|
left
|
|
|
|
middle
|
|
|
|
actionable
|
|
|
|
>
|
|
|
|
<span>Short ID </span>
|
|
|
|
</TableTh>
|
|
|
|
<TableTh xs="60" padding="0" />
|
|
|
|
</TableTr>
|
|
|
|
</TableThead>
|
|
|
|
<TableTbody>{children}</TableTbody>
|
|
|
|
{!noInstances ? (
|
|
|
|
<PaginationTableFoot colSpan="6">
|
|
|
|
<PaginationItem
|
|
|
|
to={`${Global().pathname}?${queryString.stringify({
|
|
|
|
...Global().query,
|
|
|
|
limit,
|
|
|
|
offset: offset - limit
|
|
|
|
})}`}
|
|
|
|
component={Link}
|
|
|
|
disabled={isFirst}
|
|
|
|
prev
|
|
|
|
>
|
|
|
|
Prev
|
|
|
|
</PaginationItem>
|
|
|
|
{currPage - 2 > 0 ? (
|
|
|
|
<Fragment>
|
|
|
|
{currPage - 2 > 1 ? (
|
|
|
|
<PaginationItem
|
|
|
|
to={`${Global().pathname}?${queryString.stringify({
|
|
|
|
...Global().query,
|
|
|
|
limit,
|
|
|
|
offset: 0
|
|
|
|
})}`}
|
|
|
|
component={Link}
|
|
|
|
>
|
|
|
|
1
|
|
|
|
</PaginationItem>
|
|
|
|
) : null}
|
|
|
|
<PaginationItem disabled>...</PaginationItem>
|
|
|
|
</Fragment>
|
|
|
|
) : null}
|
|
|
|
{currPage > 1 ? (
|
|
|
|
<PaginationItem
|
|
|
|
to={`${Global().pathname}?${queryString.stringify({
|
|
|
|
...Global().query,
|
|
|
|
limit,
|
|
|
|
offset: offset - limit
|
|
|
|
})}`}
|
|
|
|
component={Link}
|
|
|
|
>
|
|
|
|
{currPage - 1}
|
|
|
|
</PaginationItem>
|
|
|
|
) : null}
|
|
|
|
<PaginationItem active>{currPage}</PaginationItem>
|
|
|
|
{numPages > currPage ? (
|
|
|
|
<PaginationItem
|
|
|
|
to={`${Global().pathname}?${queryString.stringify({
|
|
|
|
...Global().query,
|
|
|
|
limit,
|
|
|
|
offset: offset + limit
|
|
|
|
})}`}
|
|
|
|
component={Link}
|
|
|
|
>
|
|
|
|
{currPage + 1}
|
|
|
|
</PaginationItem>
|
|
|
|
) : null}
|
|
|
|
{currPage + 2 <= numPages ? (
|
|
|
|
<Fragment>
|
|
|
|
<PaginationItem disabled>...</PaginationItem>
|
|
|
|
{numPages - currPage > 2 ? (
|
|
|
|
<PaginationItem
|
|
|
|
to={`${Global().pathname}?${queryString.stringify({
|
|
|
|
...Global().query,
|
|
|
|
limit,
|
|
|
|
offset: total - limit
|
|
|
|
})}`}
|
|
|
|
component={Link}
|
|
|
|
>
|
|
|
|
{numPages}
|
|
|
|
</PaginationItem>
|
|
|
|
) : null}
|
|
|
|
</Fragment>
|
|
|
|
) : null}
|
|
|
|
<PaginationItem
|
|
|
|
to={`${Global().pathname}?${queryString.stringify({
|
|
|
|
...Global().query,
|
|
|
|
limit,
|
|
|
|
offset: offset + limit
|
|
|
|
})}`}
|
|
|
|
component={Link}
|
|
|
|
disabled={isLast}
|
|
|
|
next
|
|
|
|
>
|
|
|
|
Next
|
|
|
|
</PaginationItem>
|
|
|
|
</PaginationTableFoot>
|
|
|
|
) : null}
|
|
|
|
</Table>
|
|
|
|
</form>
|
|
|
|
);
|
|
|
|
};
|